/*
 * Copyright 2017, Google Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.opencensus.contrib.agent;

import static com.google.common.truth.Truth.assertThat;

import io.grpc.Context;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.After;
import org.junit.Test;

/**
 * Integration tests for {@link ThreadInstrumentation}.
 *
 * <p>The integration tests are executed in a separate JVM that has the OpenCensus agent enabled
 * via the {@code -javaagent} command line option.
 */
public class ThreadInstrumentationTest {

  private static final Context.Key<String> KEY = Context.key("mykey");

  private Context previousContext;

  @After
  public void afterMethod() {
    Context.current().detach(previousContext);
  }

  @Test(timeout = 5000)
  public void start_Runnable() throws Exception {
    final Thread callerThread = Thread.currentThread();
    final Context context = Context.current().withValue(KEY, "myvalue");
    previousContext = context.attach();

    final AtomicBoolean tested = new AtomicBoolean(false);

    Thread thread = new Thread(new Runnable() {
      @Override
      public void run() {
        assertThat(Thread.currentThread()).isNotSameAs(callerThread);
        assertThat(Context.current()).isSameAs(context);
        assertThat(KEY.get()).isEqualTo("myvalue");
        tested.set(true);
      }
    });

    thread.start();
    thread.join();

    assertThat(tested.get()).isTrue();
  }

  @Test(timeout = 5000)
  public void start_Subclass() throws Exception {
    final Thread callerThread = Thread.currentThread();
    final Context context = Context.current().withValue(KEY, "myvalue");
    previousContext = context.attach();

    final AtomicBoolean tested = new AtomicBoolean(false);

    Thread thread = new Thread() {
      @Override
      public void run() {
        assertThat(Thread.currentThread()).isNotSameAs(callerThread);
        assertThat(Context.current()).isSameAs(context);
        assertThat(KEY.get()).isEqualTo("myvalue");
        tested.set(true);
      }
    };

    thread.start();
    thread.join();

    assertThat(tested.get()).isTrue();
  }
}

@@ -732,28 +732,131 @@
 ent.
-atomic.AtomicBoolean
+Executor;%0Aimport java.util.concurrent.atomic.AtomicBoolean;%0Aimport java.util.logging.Level;%0Aimport java.util.logging.Logger
 ;%0Aim
@@ -2691,32 +2691,1524 @@
 thread.join();%0A%0A
+    assertThat(tested.get()).isTrue();%0A  %7D%0A%0A  @Test(timeout = 5000)%0A  public void start_wrappedRunnable() throws Exception %7B%0A    final Thread callerThread = Thread.currentThread();%0A    final Context context = Context.current().withValue(KEY, %22myvalue%22);%0A    previousContext = context.attach();%0A%0A    final AtomicBoolean tested = new AtomicBoolean(false);%0A%0A    Executor newThreadExecutor = new Executor() %7B%0A      @Override%0A      public void execute(Runnable command) %7B%0A        // Attach a new context before starting a new thread. This new context will be propagated to%0A        // the new thread as in #start_Runnable. However, since the Runnable has been wrapped in a%0A        // different context (by automatic instrumentation of Executor#execute), that context will%0A        // be attached when executing the Runnable.%0A        Context context2 = Context.current().withValue(KEY, %22wrong context%22);%0A        context2.attach();%0A        Thread thread = new Thread(command);%0A        thread.start();%0A        try %7B%0A          thread.join();%0A        %7D catch (InterruptedException ex) %7B%0A          Thread.currentThread().interrupt();%0A        %7D%0A        context2.detach(context);%0A      %7D%0A    %7D;%0A%0A    newThreadExecutor.execute(new Runnable() %7B%0A      @Override%0A      public void run() %7B%0A        assertThat(Thread.currentThread()).isNotSameAs(callerThread);%0A        assertThat(Context.current()).isSameAs(context);%0A        assertThat(KEY.get()).isEqualTo(%22myvalue%22);%0A        tested.set(true);%0A      %7D%0A    %7D);%0A%0A
     assertThat(t

Add a test for mixed Thread/Executor context propagation and assert that the Runnable is executed with the expected context.