1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.util.test;
18
19 import io.netty.util.LeakPresenceDetector;
20 import org.junit.jupiter.api.extension.AfterAllCallback;
21 import org.junit.jupiter.api.extension.AfterEachCallback;
22 import org.junit.jupiter.api.extension.BeforeAllCallback;
23 import org.junit.jupiter.api.extension.BeforeEachCallback;
24 import org.junit.jupiter.api.extension.ExtensionContext;
25
26 import java.util.concurrent.TimeUnit;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public final class LeakPresenceExtension
46 implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback, AfterAllCallback {
47
48 private static final Object SCOPE_KEY = new Object();
49 private static final Object PREVIOUS_SCOPE_KEY = new Object();
50
51 static {
52 System.setProperty("io.netty.customResourceLeakDetector", WithTransferableScope.class.getName());
53 }
54
55 @Override
56 public void beforeAll(ExtensionContext context) {
57 ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.GLOBAL);
58 if (store.get(SCOPE_KEY) != null) {
59 throw new IllegalStateException("Weird context lifecycle");
60 }
61 LeakPresenceDetector.ResourceScope scope = new LeakPresenceDetector.ResourceScope(context.getDisplayName());
62 store.put(SCOPE_KEY, scope);
63
64 WithTransferableScope.SCOPE.set(scope);
65 }
66
67 @Override
68 public void beforeEach(ExtensionContext context) {
69 LeakPresenceDetector.ResourceScope outerScope;
70 ExtensionContext outerContext = context;
71 while (true) {
72 outerScope = (LeakPresenceDetector.ResourceScope)
73 outerContext.getStore(ExtensionContext.Namespace.GLOBAL).get(SCOPE_KEY);
74 if (outerScope != null) {
75 break;
76 }
77 outerContext = outerContext.getParent()
78 .orElseThrow(() -> new IllegalStateException("No resource scope found"));
79 }
80
81 LeakPresenceDetector.ResourceScope previousScope = WithTransferableScope.SCOPE.get();
82 WithTransferableScope.SCOPE.set(outerScope);
83 if (previousScope != null) {
84 context.getStore(ExtensionContext.Namespace.GLOBAL).put(PREVIOUS_SCOPE_KEY, previousScope);
85 }
86 }
87
88 @Override
89 public void afterEach(ExtensionContext context) {
90 LeakPresenceDetector.ResourceScope previousScope = (LeakPresenceDetector.ResourceScope)
91 context.getStore(ExtensionContext.Namespace.GLOBAL).get(PREVIOUS_SCOPE_KEY);
92 if (previousScope != null) {
93 WithTransferableScope.SCOPE.set(previousScope);
94 }
95 }
96
97 @Override
98 public void afterAll(ExtensionContext context) throws InterruptedException {
99 LeakPresenceDetector.ResourceScope scope =
100 (LeakPresenceDetector.ResourceScope) context.getStore(ExtensionContext.Namespace.GLOBAL).get(SCOPE_KEY);
101
102
103 long start = System.nanoTime();
104 while (scope.hasOpenResources() && System.nanoTime() - start < TimeUnit.SECONDS.toNanos(5)) {
105 TimeUnit.MILLISECONDS.sleep(100);
106 }
107
108 scope.close();
109 }
110
111 public static final class WithTransferableScope<T> extends LeakPresenceDetector<T> {
112 static final InheritableThreadLocal<ResourceScope> SCOPE = new InheritableThreadLocal<>();
113
114 @SuppressWarnings("unused")
115 public WithTransferableScope(Class<?> resourceType, int samplingInterval) {
116 super(resourceType);
117 }
118
119 @SuppressWarnings("unused")
120 public WithTransferableScope(Class<?> resourceType, int samplingInterval, long maxActive) {
121 super(resourceType);
122 }
123
124 @Override
125 protected ResourceScope currentScope() throws AllocationProhibitedException {
126 ResourceScope scope = SCOPE.get();
127 if (scope == null) {
128 throw new AllocationProhibitedException("Resource created outside test?");
129 }
130 return scope;
131 }
132 }
133 }