1 /* 2 * Copyright 2019 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import java.util.ArrayList; 22 import java.util.List; 23 import java.util.logging.Handler; 24 import java.util.logging.LogRecord; 25 import java.util.logging.Logger; 26 import org.junit.After; 27 import org.junit.Before; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 import org.junit.runners.JUnit4; 31 32 @RunWith(JUnit4.class) 33 public final class ThreadLocalContextStorageTest { 34 private static final Context.Key<Object> KEY = Context.key("test-key"); 35 private static final ThreadLocalContextStorage storage = new ThreadLocalContextStorage(); 36 37 private Context contextBeforeTest; 38 saveContext()39 @Before public void saveContext() { 40 contextBeforeTest = storage.doAttach(Context.ROOT); 41 } 42 restoreContext()43 @After public void restoreContext() { 44 storage.detach(Context.ROOT, contextBeforeTest); 45 } 46 47 @Test detach_threadLocalClearedOnRoot()48 public void detach_threadLocalClearedOnRoot() { 49 Context context = Context.ROOT.withValue(KEY, new Object()); 50 Context old = storage.doAttach(context); 51 assertThat(storage.current()).isSameInstanceAs(context); 52 assertThat(ThreadLocalContextStorage.localContext.get()).isSameInstanceAs(context); 53 storage.detach(context, old); 54 // thread local must contain null to avoid leaking our ClassLoader via ROOT 55 assertThat(ThreadLocalContextStorage.localContext.get()).isNull(); 56 } 57 58 @Test detach_detachRoot()59 public void detach_detachRoot() { 60 final List<LogRecord> logs = new ArrayList<>(); 61 Handler handler = new Handler() { 62 @Override public void publish(LogRecord record) { 63 logs.add(record); 64 } 65 66 @Override public void flush() {} 67 68 @Override public void close() {} 69 }; 70 71 // Explicitly choose ROOT as the current context 72 Context context = Context.ROOT; 73 Context old = storage.doAttach(context); 74 75 // Attach and detach a random context 76 Context innerContext = Context.ROOT.withValue(KEY, new Object()); 77 storage.detach(innerContext, storage.doAttach(innerContext)); 78 79 Logger logger = Logger.getLogger(ThreadLocalContextStorage.class.getName()); 80 logger.addHandler(handler); 81 try { 82 // Make sure detaching ROOT doesn't log a warning 83 storage.detach(context, old); 84 } finally { 85 logger.removeHandler(handler); 86 } 87 assertThat(logs).isEmpty(); 88 } 89 } 90