From f40b37b48e1bcc3e3bece3ef973e04524151f004 Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Tue, 13 Feb 2018 15:29:52 +0100 Subject: [PATCH] tagging release 1.3 --- CHANGELOG.md | 7 + README.md | 6 +- agent/pom.xml | 8 +- api/pom.xml | 2 +- .../timetest/DefaultTimeProvider.java | 79 +++++++ .../devexperts/timetest/TestTimeProvider.java | 52 ++--- .../com/devexperts/timetest/TimeProvider.java | 91 +------- core/pom.xml | 6 +- .../java/com/devexperts/timetest/Methods.java | 48 ++++- .../timetest/TimeTestAgentRunner.java | 10 +- .../timetest/WeakIdentityHashSet.java | 199 ++++++++++++++++++ pom.xml | 39 ++-- test/pom.xml | 29 +-- .../devexperts/timetest/test/MyThread.java | 32 +++ .../timetest/{ => test}/Repeat.java | 2 +- .../timetest/{ => test}/RepeatRule.java | 2 +- .../timetest/test/TestTimeProviderTest.java | 4 +- .../timetest/test/TimeProviderScopeTest.java | 46 ++++ transformer/pom.xml | 6 +- .../devexperts/timetest/TimeTestAgent.java | 2 + .../ChangeTimeMethodsMethodTransformer.java | 4 +- .../{ => transformer}/Configuration.java | 16 +- .../EntryPointsAdder.java} | 24 ++- .../timetest/{ => transformer}/GlobUtil.java | 2 +- .../transformer/ThreadStartTracer.java | 48 +++++ .../TimeTestTransformer.java | 31 ++- .../TransformationUtils.java | 12 +- 27 files changed, 604 insertions(+), 203 deletions(-) create mode 100644 api/src/main/java/com/devexperts/timetest/DefaultTimeProvider.java create mode 100644 core/src/main/java/com/devexperts/timetest/WeakIdentityHashSet.java create mode 100644 test/src/test/java/com/devexperts/timetest/test/MyThread.java rename test/src/test/java/com/devexperts/timetest/{ => test}/Repeat.java (96%) rename test/src/test/java/com/devexperts/timetest/{ => test}/RepeatRule.java (97%) create mode 100644 test/src/test/java/com/devexperts/timetest/test/TimeProviderScopeTest.java rename transformer/src/main/java/com/devexperts/timetest/{ => transformer}/ChangeTimeMethodsMethodTransformer.java (97%) rename transformer/src/main/java/com/devexperts/timetest/{ => transformer}/Configuration.java (80%) rename transformer/src/main/java/com/devexperts/timetest/{TestEnterPointsAdder.java => transformer/EntryPointsAdder.java} (65%) rename transformer/src/main/java/com/devexperts/timetest/{ => transformer}/GlobUtil.java (97%) create mode 100644 transformer/src/main/java/com/devexperts/timetest/transformer/ThreadStartTracer.java rename transformer/src/main/java/com/devexperts/timetest/{ => transformer}/TimeTestTransformer.java (77%) rename transformer/src/main/java/com/devexperts/timetest/{ => transformer}/TransformationUtils.java (72%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fff8cb..9c12cd8 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +1.3 - 2018-02-13 +---------------- +* Change a strategy to define if we are executing in the testing code or not. A new strategy uses entry points (see 'timetest.testingCode' property) to understand that we are executing in the testing code and traces Thread.start() invocations to mark new threads as 'in testing code' too. +* Use Dl-Check in testing phase +* Move default time provider to separate class for better logging +* ove dependency JARs to time-test's folder in order not to conflict with other agents + 1.2 - 2017-04-24 ---------------- * Fix deadlock in TestTimeProvider diff --git a/README.md b/README.md index 4de9fdb..638d0fe 100755 --- a/README.md +++ b/README.md @@ -47,17 +47,19 @@ Use `TestTimeProvider.setTime(millis)` and `TestTimeProvider.inscreaseTime(milli to change current time. Use `TestTimeProvider.waitUntilThreadsAreFrozen` to wait until all threads complete their work. +In order to work properly **TestTimeProvider** defines if it is executed in the testing code or not on every time-based operation invocation (including `Object.notity()` and similars). For this purpose an entry point to your code have to be specified (see **timetest.testingCode** property). After that, if your code starts a thread it will be marked as ours too (**time-test** traces Thread.start() invocations for this purpose). However, there are some problems if you use shared scheduler like `ForkJoinPool`. In order to work with it expand **timetest.testingCode** property. # Configuration You can pass your own configuration in `timetest.properties` properties file or set these properties as system parameters (*-Dparam.name=value*). The `timetest.properties` file should be in the application classpath. -* **timetest.tests** - defines test entrance classes in glob format. Usually, test classes. Default value: *\*Test* (all classes with suffix *Test*) +* **timetest.testingCode** - defines the entry points of the testing code in glob format. Several globs can be separated by comma. Default value: *com.devexperts.\*Test* (all classes with *Test* suffix). +* **timetest.nonTestingCode** - defines the scope of code which have to be processed like non-testing onecode in glob format. It can be helpful to print real timestamps in logging instead of virtual ones. Default value: *com.devexperts.logging.\**. * ***timetest.log.level*** defines internal logging level. Possible values: *DEBUG*, *INFO* (default value), *WARN*, *ERROR*. * ***timetest.log.file*** defines path of file to be used for logging. By default logs are printed to the standard output. * ***timetest.cache.dir*** [experimental] defines directory to be used for transformed classes caching. This feature is unstable, use it on your own risk. * ***timetest.include*** defines the transformation scope using globs. For example, setting the value to ```package.to.transform.*,another.package.to.transform.*``` informs **time-test** to transform classes from these packages only. By default all classes are included. -* ***timetest.exclude*** defines the classes which should be excluded from transformation. The syntax is similar to **timetest.include** option. +* ***timetest.exclude*** defines the classes which should be excluded from transformation. The syntax is similar to **timetest.include** option. Default value: *org.apache.maven.\*,org.junit.\*,com.devexperts.test.\** # Maven diff --git a/agent/pom.xml b/agent/pom.xml index 4b77ef6..4d6bc78 100755 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -3,7 +3,7 @@ timetest com.devexperts.timetest - 1.3-SNAPSHOT + 1.3 4.0.0 @@ -28,7 +28,7 @@ asm-all,owner,owner-java8,transformer,jagent-impl - ${project.build.outputDirectory} + ${project.build.outputDirectory}/timetest true @@ -64,13 +64,13 @@ com.devexperts.timetest core ${project.version} - + provided com.devexperts.timetest transformer ${project.version} - + provided diff --git a/api/pom.xml b/api/pom.xml index 1000d7d..f58a84a 100755 --- a/api/pom.xml +++ b/api/pom.xml @@ -3,7 +3,7 @@ timetest com.devexperts.timetest - 1.3-SNAPSHOT + 1.3 4.0.0 diff --git a/api/src/main/java/com/devexperts/timetest/DefaultTimeProvider.java b/api/src/main/java/com/devexperts/timetest/DefaultTimeProvider.java new file mode 100644 index 0000000..11c529c --- /dev/null +++ b/api/src/main/java/com/devexperts/timetest/DefaultTimeProvider.java @@ -0,0 +1,79 @@ +package com.devexperts.timetest; + +/* + * #%L + * api + * %% + * Copyright (C) 2015 - 2018 Devexperts, LLC + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.devexperts.util.UnsafeHolder; + +public class DefaultTimeProvider extends TimeProvider { + @Override + public long timeMillis() { + return System.currentTimeMillis(); + } + + @Override + public long nanoTime() { + return System.nanoTime(); + } + + @Override + public void sleep(long millis) throws InterruptedException { + Thread.sleep(millis); + } + + @Override + public void sleep(long millis, int nanos) throws InterruptedException { + Thread.sleep(millis, nanos); + } + + @SuppressWarnings({"WaitWhileNotSynced", "WaitNotInLoop"}) + @Override + public void waitOn(Object monitor, long millis) throws InterruptedException { + monitor.wait(millis); + } + + @SuppressWarnings({"WaitWhileNotSynced", "WaitNotInLoop"}) + @Override + public void waitOn(Object monitor, long millis, int nanos) throws InterruptedException { + monitor.wait(millis, nanos); + } + + @Override + public void notifyAll(Object monitor) { + monitor.notifyAll(); + } + + @Override + public void notify(Object monitor) { + monitor.notify(); + } + + @Override + public void park(boolean isAbsolute, long time) { + UnsafeHolder.UNSAFE.park(isAbsolute, time); + } + + @Override + public void unpark(Object thread) { + UnsafeHolder.UNSAFE.unpark(thread); + } +} diff --git a/api/src/main/java/com/devexperts/timetest/TestTimeProvider.java b/api/src/main/java/com/devexperts/timetest/TestTimeProvider.java index 29b1cd6..19e073c 100644 --- a/api/src/main/java/com/devexperts/timetest/TestTimeProvider.java +++ b/api/src/main/java/com/devexperts/timetest/TestTimeProvider.java @@ -92,10 +92,12 @@ public static synchronized void start(long startTime) { /** * Starts using {@link TestTimeProvider} instead of current. - * Sets {@link System#currentTimeMillis()} as start time. + * Sets {@link System#currentTimeMillis()} as start time and returns it. */ - public static void start() { - start(System.currentTimeMillis()); + public static long start() { + long startTime = System.currentTimeMillis(); + start(startTime); + return startTime; } /** @@ -192,27 +194,24 @@ public void notify(Object monitor) { } private synchronized void setTime0(long millis) { - synchronized (this) { - if (millis < currentTime) - throw new IllegalArgumentException( - "Time cannot be decreased, current=" + currentTime + ", new=" + millis); - currentTime = millis; - threadInfos.values().stream() - .filter(ti -> currentTime >= ti.resumeTime) - .forEach(ti -> ti.resumed = true); - waitingThreads.values().forEach(tis -> tis.removeIf(ti -> ti.resumed)); - waitingThreads.entrySet().removeIf(e -> e.getValue().isEmpty()); + if (millis < currentTime) { + throw new IllegalArgumentException( + "Time cannot be decreased, current=" + currentTime + ", new=" + millis); } + currentTime = millis; + threadInfos.values().stream() + .filter(ti -> currentTime >= ti.resumeTime) + .forEach(ti -> ti.resumed = true); + waitingThreads.values().forEach(tis -> tis.removeIf(ti -> ti.resumed)); + waitingThreads.entrySet().removeIf(e -> e.getValue().isEmpty()); } @Override public synchronized void notifyAll(Object monitor) { - synchronized (this) { - List tis = waitingThreads.remove(monitor); - if (tis == null) - return; - tis.forEach(ti -> ti.resumed = true); - } + List tis = waitingThreads.remove(monitor); + if (tis == null) + return; + tis.forEach(ti -> ti.resumed = true); } @Override @@ -246,16 +245,11 @@ public void waitOn(Object monitor, long millis, int nanos) throws InterruptedExc // Wait with small timeout until // current time is equals or greater than resume time // or notify() is called on the monitor - while (true) { + while (!ti.resumed) { monitor.wait(WAITING_TIMEOUT); - if (Thread.currentThread().isInterrupted()) - throw new InterruptedException(); - if (ti.resumed) { - synchronized (this) { - threadInfos.remove(ti.thread); - } - return; - } + } + synchronized (this) { + threadInfos.remove(ti.thread); } } @@ -293,8 +287,6 @@ public void park(boolean isAbsolute, long time) { Thread.currentThread().interrupt(); return; } - if (Thread.currentThread().isInterrupted()) - return; if (ti.resumed) { synchronized (this) { threadInfos.remove(ti.thread); diff --git a/api/src/main/java/com/devexperts/timetest/TimeProvider.java b/api/src/main/java/com/devexperts/timetest/TimeProvider.java index f3405a8..879ee96 100644 --- a/api/src/main/java/com/devexperts/timetest/TimeProvider.java +++ b/api/src/main/java/com/devexperts/timetest/TimeProvider.java @@ -22,96 +22,17 @@ * #L% */ - -import com.devexperts.util.UnsafeHolder; - /** * Provides time-based methods. Should be thread-safe. */ public abstract class TimeProvider { - - // ========== Time-based methods ========== - /** * Default time provider that uses standard system methods. */ - private static final TimeProvider DEFAULT = new TimeProvider() { - @Override - public long timeMillis() { - return System.currentTimeMillis(); - } - - @Override - public long nanoTime() { - return System.nanoTime(); - } - - @Override - public void sleep(long millis) throws InterruptedException { - Thread.sleep(millis); - } - - @Override - public void sleep(long millis, int nanos) throws InterruptedException { - Thread.sleep(millis, nanos); - } - - @SuppressWarnings({"WaitWhileNotSynced", "WaitNotInLoop"}) - @Override - public void waitOn(Object monitor, long millis) throws InterruptedException { - monitor.wait(millis); - } - - @SuppressWarnings({"WaitWhileNotSynced", "WaitNotInLoop"}) - @Override - public void waitOn(Object monitor, long millis, int nanos) throws InterruptedException { - monitor.wait(millis, nanos); - } - - @Override - public void notifyAll(Object monitor) { - monitor.notifyAll(); - } - - @Override - public void notify(Object monitor) { - monitor.notify(); - } - - @Override - public void park(boolean isAbsolute, long time) { - UnsafeHolder.UNSAFE.park(isAbsolute, time); - } - - @Override - public void unpark(Object thread) { - UnsafeHolder.UNSAFE.unpark(thread); - } - }; + private static final TimeProvider DEFAULT = new DefaultTimeProvider(); private volatile static TimeProvider timeProvider = DEFAULT; - // It is used for detection that we are executing in the test code, - // so the already defined {@link TimeProvider} should be used. - private static final ThreadLocal testMethodsCallStackSize = new ThreadLocal() { - @Override - protected Integer initialValue() { - return 0; - } - }; - - /** - * Should be called as the first action of test code method - */ - static void enterTestingCodeMethod() { - testMethodsCallStackSize.set(testMethodsCallStackSize.get() + 1); - } - - /** - * Should be called as the last action of test code method - */ - static void leaveTestingCodeMethod() { - testMethodsCallStackSize.set(testMethodsCallStackSize.get() - 1); - } + public static final ThreadLocal inTestingCode = ThreadLocal.withInitial(() -> false); /** * Resets time provider to {@link #DEFAULT default}. Should be used for test purpose only. @@ -121,13 +42,13 @@ static void resetTimeProvider() { } /** - * Returns current time provider if this method is called from test method (see configuration) - * or {@link #DEFAULT} otherwise. + * Returns current time provider if this method is called from the testing code + * (see configuration), {@link #DEFAULT} otherwise. * * @return current time provider. */ public static TimeProvider getTimeProvider() { - return testMethodsCallStackSize.get() > 0 ? timeProvider : DEFAULT; + return inTestingCode.get() ? timeProvider : DEFAULT; } /** @@ -139,6 +60,8 @@ static void setTimeProvider(TimeProvider timeProvider) { TimeProvider.timeProvider = timeProvider; } + // ========== Time-based methods ========== + /** * @see System#currentTimeMillis() */ diff --git a/core/pom.xml b/core/pom.xml index 3c367d8..e0b493f 100755 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,16 +3,12 @@ timetest com.devexperts.timetest - 1.3-SNAPSHOT + 1.3 4.0.0 core - - true - - diff --git a/core/src/main/java/com/devexperts/timetest/Methods.java b/core/src/main/java/com/devexperts/timetest/Methods.java index b60de48..d43cecb 100644 --- a/core/src/main/java/com/devexperts/timetest/Methods.java +++ b/core/src/main/java/com/devexperts/timetest/Methods.java @@ -25,54 +25,94 @@ @SuppressWarnings("unused") // used in transformer public class Methods { public static long timeMillis() { + checkNewThread(); return TimeProvider.getTimeProvider().timeMillis(); } public static long nanoTime() { + checkNewThread(); return TimeProvider.getTimeProvider().nanoTime(); } public static void sleep(long millis) throws InterruptedException { + checkNewThread(); TimeProvider.getTimeProvider().sleep(millis); } public static void sleep(long millis, int nanos) throws InterruptedException { + checkNewThread(); TimeProvider.getTimeProvider().sleep(millis, nanos); } public static void waitOn(Object monitor) throws InterruptedException { + checkNewThread(); TimeProvider.getTimeProvider().waitOn(monitor, 0); } public static void waitOn(Object monitor, long millis) throws InterruptedException { + checkNewThread(); TimeProvider.getTimeProvider().waitOn(monitor, millis); } public static void waitOn(Object monitor, long millis, int nanos) throws InterruptedException { + checkNewThread(); TimeProvider.getTimeProvider().waitOn(monitor, millis, nanos); } public static void notify(Object monitor) { + checkNewThread(); TimeProvider.getTimeProvider().notify(monitor); } public static void notifyAll(Object monitor) { + checkNewThread(); TimeProvider.getTimeProvider().notifyAll(monitor); } public static void park(boolean isAbsolute, long time) { + checkNewThread(); TimeProvider.getTimeProvider().park(isAbsolute, time); } public static void unpark(Object thread) { + checkNewThread(); TimeProvider.getTimeProvider().unpark(thread); } - public static void enterTestingCodeMethod() { - TimeProvider.enterTestingCodeMethod(); + public static boolean isInTestingCode() { + return TimeProvider.inTestingCode.get(); } - public static void leaveTestingCodeMethod() { - TimeProvider.leaveTestingCodeMethod(); + public static void enterTestingCode(boolean isInTestingCodeAlready) { + if (!isInTestingCodeAlready) + TimeProvider.inTestingCode.set(true); + } + + public static void leaveTestingCode(boolean isInTestingCodeAlready) { + if (!isInTestingCodeAlready) + TimeProvider.inTestingCode.set(false); + } + + public static void enterNonTestingCode(boolean isInTestingCodeAlready) { + if (isInTestingCodeAlready) + TimeProvider.inTestingCode.set(false); + } + + public static void leaveNonTestingCode(boolean isInTestingCodeAlready) { + if (isInTestingCodeAlready) + TimeProvider.inTestingCode.set(true); + } + + private static WeakIdentityHashSet NEW_THREADS_FROM_TESTING_CODE = new WeakIdentityHashSet<>(); + + public static void startThread(Thread thread) { + if (isInTestingCode()) { + NEW_THREADS_FROM_TESTING_CODE.add(thread); + } + } + + private static void checkNewThread() { + if (NEW_THREADS_FROM_TESTING_CODE.remove(Thread.currentThread())) + TimeProvider.inTestingCode.set(true); } } diff --git a/core/src/main/java/com/devexperts/timetest/TimeTestAgentRunner.java b/core/src/main/java/com/devexperts/timetest/TimeTestAgentRunner.java index 2c36f48..601d85f 100644 --- a/core/src/main/java/com/devexperts/timetest/TimeTestAgentRunner.java +++ b/core/src/main/java/com/devexperts/timetest/TimeTestAgentRunner.java @@ -33,11 +33,11 @@ public static void premain(String agentArgs, Instrumentation inst) throws Except // Run "SampleAgent" using "JAgentRunner". "SampleAgent" is loaded via created "InnerJarClassLoader". JAgentRunner.runAgent("com.devexperts.timetest.TimeTestAgent", inst, agentArgs, InnerJarClassLoader.createForJars( - "asm-all.jar", - "jagent-impl.jar", - "transformer.jar", - "owner.jar", - "owner-java8.jar" + "timetest/asm-all.jar", + "timetest/jagent-impl.jar", + "timetest/transformer.jar", + "timetest/owner.jar", + "timetest/owner-java8.jar" )); } } diff --git a/core/src/main/java/com/devexperts/timetest/WeakIdentityHashSet.java b/core/src/main/java/com/devexperts/timetest/WeakIdentityHashSet.java new file mode 100644 index 0000000..5b19a06 --- /dev/null +++ b/core/src/main/java/com/devexperts/timetest/WeakIdentityHashSet.java @@ -0,0 +1,199 @@ +package com.devexperts.timetest; + +/* + * #%L + * core + * %% + * Copyright (C) 2015 - 2016 Devexperts, LLC + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicReferenceArray; + +public class WeakIdentityHashSet implements Iterable { + private static final int MAGIC = 0xB46394CD; + private static final int MAX_SHIFT = 29; + private static final int THRESHOLD = (int)((1L << 32) * 0.5); // 50% fill factor for speed + + private static final WeakReference EMPTY_KEY = new WeakReference(null); + + private static class Core { + final int shift; + final int length; + final AtomicReferenceArray> keys; + int size; + + Core(int shift) { + this.shift = shift; + length = 1 << (32 - shift); + keys = new AtomicReferenceArray<>(length); + } + } + + private volatile Core core = new Core<>(MAX_SHIFT); + + // does not need external synchronization + public boolean contains(T key) { + Core core = this.core; + if (core.size == 0) + return false; + int i = (System.identityHashCode(key) * MAGIC) >>> core.shift; + while (true) { + WeakReference r = core.keys.get(i); + if (r == null) + return false; + if (r.get() == key) + return true; + if (i == 0) + i = core.length; + i--; + } + } + + // needs external synchronization + public synchronized boolean add(T key) { + boolean res = addInternal(this.core, key); + if (res) + if (core.size >= (THRESHOLD >>> core.shift)) + rehash(); + return res; + } + + // needs external synchronization + public boolean remove(T o) { + if (core.size == 0) + return false; + synchronized (this) { + int i = (System.identityHashCode(o) * MAGIC) >>> core.shift; + if (core.size == 0) + return false; + while (true) { + WeakReference r = core.keys.get(i); + if (r == null) + return false; + if (r.get() == o) { + core.keys.set(i, EMPTY_KEY); + return true; + } + if (i == 0) + i = core.length; + i--; + } + } + } + + private boolean addInternal(Core core, T key) { + int i = (System.identityHashCode(key) * MAGIC) >>> core.shift; + int startI = i; + int firstEmptyKeyIndex = -1; + while (true) { + WeakReference k = core.keys.get(i); + if (k == null) { + if (firstEmptyKeyIndex != -1) { + core.keys.set(firstEmptyKeyIndex, new WeakReference<>(key)); + } else { + core.keys.set(i, new WeakReference<>(key)); + core.size++; + } + if (firstEmptyKeyIndex != -1) { + while (true) { + if (i == startI) + break; + i++; + if (i == core.length) + i = 0; + if (core.keys.get(i) == EMPTY_KEY) { + core.keys.set(i, null); + core.size--; + } else { + break; + } + } + } + return true; + } else if (k.get() == key) { + return false; + } + if (k.get() == null) { + core.keys.set(i, EMPTY_KEY); + if (firstEmptyKeyIndex == -1) + firstEmptyKeyIndex = i; + } else if (k == EMPTY_KEY) { + if (firstEmptyKeyIndex == -1) + firstEmptyKeyIndex = i; + } + if (i == 0) + i = core.length; + i--; + } + } + + private void rehash() { + Core oldCore = core; + Core newCore = new Core<>(oldCore.shift - 1); + for (int i = 0; i < oldCore.length; i++) + if (oldCore.keys.get(i) != null) { + T key = oldCore.keys.get(i).get(); + if (key != null) { + newCore.size++; + addInternal(newCore, key); + } + } + core = newCore; + } + + @Override + public Iterator iterator() { + return new Iterator0(); + } + + private class Iterator0 implements Iterator { + + private final Core core; + private int i; + private T next; + + Iterator0() { + this.core = WeakIdentityHashSet.this.core; + updateIndexToNext(); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public T next() { + T res = next; + updateIndexToNext(); + return res; + } + + @SuppressWarnings("unchecked") + private void updateIndexToNext() { + next = null; + while (i < core.length && next == null) { + if (core.keys.get(i) != null) + next = core.keys.get(i).get(); + i++; + } + } + } +} diff --git a/pom.xml b/pom.xml index 85fed9e..76a8cb8 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devexperts.timetest timetest pom - 1.3-SNAPSHOT + 1.3 time-test Utility for testing time-based functionality @@ -24,7 +24,7 @@ timetest - 1.3.2 + 1.4 @@ -54,11 +54,6 @@ license-maven-plugin 1.8 - - org.apache.maven.plugins - maven-release-plugin - 2.5.2 - org.apache.maven.plugins maven-javadoc-plugin @@ -69,6 +64,11 @@ maven-shade-plugin 2.4.3 + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + com.devexperts.bintray bintray-maven-plugin @@ -96,7 +96,7 @@ license-maven-plugin - check-headers + update-file-headers update-file-header @@ -104,13 +104,6 @@ - - org.apache.maven.plugins - maven-release-plugin - - release - - @@ -145,7 +138,7 @@ check-file-header - package + verify @@ -173,6 +166,20 @@ + + + com.devexperts.jgitflow + jgitflow-maven-plugin + 1.0-m5.1-devexperts + + true + + timetest- + + true + [release] + + diff --git a/test/pom.xml b/test/pom.xml index 4d0e878..a0a3de5 100755 --- a/test/pom.xml +++ b/test/pom.xml @@ -3,7 +3,7 @@ timetest com.devexperts.timetest - 1.3-SNAPSHOT + 1.3 4.0.0 @@ -11,6 +11,7 @@ true + 0.5.1 @@ -28,18 +29,18 @@ - com.devexperts.timetest + com.devexperts.dlcheck agent - ${project.version} + ${dlcheck.version} ${project.build.directory} - ${agent.artifact.name}.jar + dlcheck.jar - com.devexperts.dlcheck + com.devexperts.timetest agent - 0.4 + ${project.version} ${project.build.directory} - dlcheck.jar + ${agent.artifact.name}.jar @@ -53,9 +54,11 @@ 2.19.1 + -javaagent:${project.build.directory}/dlcheck.jar + -Ddlcheck.fail=true -javaagent:${project.build.directory}/${agent.artifact.name}.jar - -Dtimetest.log.level=DEBUG - -Dtimetest.dump.dir=${project.build.directory}/timetest_dump + + @@ -66,8 +69,8 @@ com.devexperts.dlcheck agent - 0.4 - provided + ${dlcheck.version} + test com.devexperts.timetest @@ -88,6 +91,4 @@ test - - - \ No newline at end of file + diff --git a/test/src/test/java/com/devexperts/timetest/test/MyThread.java b/test/src/test/java/com/devexperts/timetest/test/MyThread.java new file mode 100644 index 0000000..efd84aa --- /dev/null +++ b/test/src/test/java/com/devexperts/timetest/test/MyThread.java @@ -0,0 +1,32 @@ +package com.devexperts.timetest.test; + +/* + * #%L + * test + * %% + * Copyright (C) 2015 - 2017 Devexperts, LLC + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +public class MyThread extends Thread { + public long time; + + @Override + public void run() { + this.time = System.currentTimeMillis(); + } +} diff --git a/test/src/test/java/com/devexperts/timetest/Repeat.java b/test/src/test/java/com/devexperts/timetest/test/Repeat.java similarity index 96% rename from test/src/test/java/com/devexperts/timetest/Repeat.java rename to test/src/test/java/com/devexperts/timetest/test/Repeat.java index c5e24ff..2e49f73 100644 --- a/test/src/test/java/com/devexperts/timetest/Repeat.java +++ b/test/src/test/java/com/devexperts/timetest/test/Repeat.java @@ -1,4 +1,4 @@ -package com.devexperts.timetest; +package com.devexperts.timetest.test; /* * #%L diff --git a/test/src/test/java/com/devexperts/timetest/RepeatRule.java b/test/src/test/java/com/devexperts/timetest/test/RepeatRule.java similarity index 97% rename from test/src/test/java/com/devexperts/timetest/RepeatRule.java rename to test/src/test/java/com/devexperts/timetest/test/RepeatRule.java index 459bcde..252377b 100644 --- a/test/src/test/java/com/devexperts/timetest/RepeatRule.java +++ b/test/src/test/java/com/devexperts/timetest/test/RepeatRule.java @@ -1,4 +1,4 @@ -package com.devexperts.timetest; +package com.devexperts.timetest.test; /* * #%L diff --git a/test/src/test/java/com/devexperts/timetest/test/TestTimeProviderTest.java b/test/src/test/java/com/devexperts/timetest/test/TestTimeProviderTest.java index e4a7cac..2653d81 100644 --- a/test/src/test/java/com/devexperts/timetest/test/TestTimeProviderTest.java +++ b/test/src/test/java/com/devexperts/timetest/test/TestTimeProviderTest.java @@ -23,8 +23,6 @@ */ -import com.devexperts.timetest.Repeat; -import com.devexperts.timetest.RepeatRule; import com.devexperts.timetest.TestTimeProvider; import com.devexperts.util.UnsafeHolder; import org.junit.After; @@ -270,7 +268,7 @@ public void testUnsafePark() throws InterruptedException { } }, "TestThread"); thread.start(); - TestTimeProvider.waitUntilThreadsAreFrozen(200); + TestTimeProvider.waitUntilThreadsAreFrozen(1000); synchronized (owner) { UnsafeHolder.UNSAFE.unpark(thread); owner.wait(Long.MAX_VALUE); diff --git a/test/src/test/java/com/devexperts/timetest/test/TimeProviderScopeTest.java b/test/src/test/java/com/devexperts/timetest/test/TimeProviderScopeTest.java new file mode 100644 index 0000000..4a1ec81 --- /dev/null +++ b/test/src/test/java/com/devexperts/timetest/test/TimeProviderScopeTest.java @@ -0,0 +1,46 @@ +package com.devexperts.timetest.test; + +/* + * #%L + * test + * %% + * Copyright (C) 2015 - 2017 Devexperts, LLC + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.devexperts.timetest.TestTimeProvider; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class TimeProviderScopeTest { + + @After + public void tearDown() { + TestTimeProvider.reset(); + } + + @Test + public void testTimeProviderInAnotherThread() throws InterruptedException { + long time = 100; + TestTimeProvider.start(time); + MyThread thread = new MyThread(); + thread.start(); + thread.join(); + Assert.assertEquals(time, thread.time); + } +} diff --git a/transformer/pom.xml b/transformer/pom.xml index a71d7c1..983f748 100755 --- a/transformer/pom.xml +++ b/transformer/pom.xml @@ -3,16 +3,12 @@ timetest com.devexperts.timetest - 1.3-SNAPSHOT + 1.3 4.0.0 transformer - - true - - com.devexperts.jagent diff --git a/transformer/src/main/java/com/devexperts/timetest/TimeTestAgent.java b/transformer/src/main/java/com/devexperts/timetest/TimeTestAgent.java index b6408f8..60da2eb 100644 --- a/transformer/src/main/java/com/devexperts/timetest/TimeTestAgent.java +++ b/transformer/src/main/java/com/devexperts/timetest/TimeTestAgent.java @@ -26,6 +26,8 @@ import com.devexperts.jagent.JAgent; import com.devexperts.jagent.JAgentUtil; import com.devexperts.jagent.Log; +import com.devexperts.timetest.transformer.Configuration; +import com.devexperts.timetest.transformer.TimeTestTransformer; import org.aeonbits.owner.ConfigFactory; import java.lang.instrument.Instrumentation; diff --git a/transformer/src/main/java/com/devexperts/timetest/ChangeTimeMethodsMethodTransformer.java b/transformer/src/main/java/com/devexperts/timetest/transformer/ChangeTimeMethodsMethodTransformer.java similarity index 97% rename from transformer/src/main/java/com/devexperts/timetest/ChangeTimeMethodsMethodTransformer.java rename to transformer/src/main/java/com/devexperts/timetest/transformer/ChangeTimeMethodsMethodTransformer.java index 319246d..d91c101 100644 --- a/transformer/src/main/java/com/devexperts/timetest/ChangeTimeMethodsMethodTransformer.java +++ b/transformer/src/main/java/com/devexperts/timetest/transformer/ChangeTimeMethodsMethodTransformer.java @@ -1,4 +1,4 @@ -package com.devexperts.timetest; +package com.devexperts.timetest.transformer; /* * #%L @@ -25,7 +25,7 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.commons.GeneratorAdapter; -import static com.devexperts.timetest.TransformationUtils.*; +import static com.devexperts.timetest.transformer.TransformationUtils.*; import static org.objectweb.asm.Opcodes.*; class ChangeTimeMethodsMethodTransformer extends MethodVisitor { diff --git a/transformer/src/main/java/com/devexperts/timetest/Configuration.java b/transformer/src/main/java/com/devexperts/timetest/transformer/Configuration.java similarity index 80% rename from transformer/src/main/java/com/devexperts/timetest/Configuration.java rename to transformer/src/main/java/com/devexperts/timetest/transformer/Configuration.java index 38516bf..24cf429 100644 --- a/transformer/src/main/java/com/devexperts/timetest/Configuration.java +++ b/transformer/src/main/java/com/devexperts/timetest/transformer/Configuration.java @@ -1,4 +1,4 @@ -package com.devexperts.timetest; +package com.devexperts.timetest.transformer; /* * #%L @@ -33,12 +33,16 @@ public interface Configuration extends Config { String[] include(); @Key("timetest.exclude") - @DefaultValue("") + @DefaultValue("org.apache.maven.*,org.junit.*,com.devexperts.test.*") String[] exclude(); - @Key("timetest.tests") - @DefaultValue("com.devexperts.*.test.*") - String[] testClasses(); + @Key("timetest.testingCode") + @DefaultValue("*Test") + String[] testingCode(); + + @Key("timetest.nonTestingCode") + @DefaultValue("com.devexperts.logging.*") + String[] nonTestingCode(); @Key("timetest.log.level") @DefaultValue("INFO") @@ -52,7 +56,7 @@ public interface Configuration extends Config { boolean verboseRedifinition(); @Key("timetest.redefinition.enabled") - @DefaultValue("false") + @DefaultValue("true") boolean redefine(); @Key("timetest.cache.dir") diff --git a/transformer/src/main/java/com/devexperts/timetest/TestEnterPointsAdder.java b/transformer/src/main/java/com/devexperts/timetest/transformer/EntryPointsAdder.java similarity index 65% rename from transformer/src/main/java/com/devexperts/timetest/TestEnterPointsAdder.java rename to transformer/src/main/java/com/devexperts/timetest/transformer/EntryPointsAdder.java index fcb0a8a..aed4d3a 100644 --- a/transformer/src/main/java/com/devexperts/timetest/TestEnterPointsAdder.java +++ b/transformer/src/main/java/com/devexperts/timetest/transformer/EntryPointsAdder.java @@ -1,4 +1,4 @@ -package com.devexperts.timetest; +package com.devexperts.timetest.transformer; /* * #%L @@ -27,27 +27,35 @@ import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; -import static com.devexperts.timetest.TransformationUtils.*; +import static com.devexperts.timetest.transformer.TransformationUtils.*; import static org.objectweb.asm.Opcodes.*; -class TestEnterPointsAdder extends MethodVisitor { +class EntryPointsAdder extends MethodVisitor { private static final Type THROWABLE_TYPE = Type.getType(Throwable.class); + private final boolean testingCode; private final GeneratorAdapter mv; private final Label tryLabel = new Label(); private final Label catchLabel = new Label(); - TestEnterPointsAdder(GeneratorAdapter mv) { + private int isInTestingCodeAlreadyLocal = -1; + + EntryPointsAdder(boolean testingCode, GeneratorAdapter mv) { super(ASM_API, mv); + this.testingCode = testingCode; this.mv = mv; } @Override public void visitCode() { super.visitCode(); + isInTestingCodeAlreadyLocal = mv.newLocal(Type.BOOLEAN_TYPE); + mv.invokeStatic(METHODS_TYPE, IS_IN_TESTING_CODE_METHOD); + mv.storeLocal(isInTestingCodeAlreadyLocal, Type.BOOLEAN_TYPE); mv.visitLabel(tryLabel); - mv.invokeStatic(METHODS_TYPE, ENTER_TRANSFORMED_METHOD); + mv.loadLocal(isInTestingCodeAlreadyLocal, Type.BOOLEAN_TYPE); + mv.invokeStatic(METHODS_TYPE, testingCode ? ENTER_TESTING_CODE_METHOD : ENTER_NON_TESTING_CODE_METHOD); } @Override @@ -59,7 +67,8 @@ public void visitInsn(int opcode) { case IRETURN: case LRETURN: case RETURN: - mv.invokeStatic(METHODS_TYPE, LEAVE_TRANSFORMED_METHOD); + mv.loadLocal(isInTestingCodeAlreadyLocal, Type.BOOLEAN_TYPE); + mv.invokeStatic(METHODS_TYPE, testingCode ? LEAVE_TESTING_CODE_METHOD : LEAVE_NON_TESTING_CODE_METHOD); mv.visitInsn(opcode); break; default: @@ -72,7 +81,8 @@ public void visitMaxs(int maxStack, int maxLocals) { mv.visitLabel(catchLabel); int throwableLocal = mv.newLocal(THROWABLE_TYPE); mv.storeLocal(throwableLocal); - mv.invokeStatic(METHODS_TYPE, LEAVE_TRANSFORMED_METHOD); + mv.loadLocal(isInTestingCodeAlreadyLocal, Type.BOOLEAN_TYPE); + mv.invokeStatic(METHODS_TYPE, testingCode ? LEAVE_TESTING_CODE_METHOD : LEAVE_NON_TESTING_CODE_METHOD); mv.loadLocal(throwableLocal); mv.throwException(); mv.visitTryCatchBlock(tryLabel, catchLabel, catchLabel, null); diff --git a/transformer/src/main/java/com/devexperts/timetest/GlobUtil.java b/transformer/src/main/java/com/devexperts/timetest/transformer/GlobUtil.java similarity index 97% rename from transformer/src/main/java/com/devexperts/timetest/GlobUtil.java rename to transformer/src/main/java/com/devexperts/timetest/transformer/GlobUtil.java index 8b84774..776ba0a 100644 --- a/transformer/src/main/java/com/devexperts/timetest/GlobUtil.java +++ b/transformer/src/main/java/com/devexperts/timetest/transformer/GlobUtil.java @@ -6,7 +6,7 @@ * If a copy of the MPL was not distributed with this file, You can obtain one at * http://mozilla.org/MPL/2.0/. */ -package com.devexperts.timetest; +package com.devexperts.timetest.transformer; /* * #%L diff --git a/transformer/src/main/java/com/devexperts/timetest/transformer/ThreadStartTracer.java b/transformer/src/main/java/com/devexperts/timetest/transformer/ThreadStartTracer.java new file mode 100644 index 0000000..4de2507 --- /dev/null +++ b/transformer/src/main/java/com/devexperts/timetest/transformer/ThreadStartTracer.java @@ -0,0 +1,48 @@ +package com.devexperts.timetest.transformer; + +/* + * #%L + * transformer + * %% + * Copyright (C) 2015 - 2018 Devexperts, LLC + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.commons.GeneratorAdapter; + +import static com.devexperts.timetest.transformer.TransformationUtils.ASM_API; +import static com.devexperts.timetest.transformer.TransformationUtils.METHODS_TYPE; +import static com.devexperts.timetest.transformer.TransformationUtils.START_THREAD_METHOD; + +public class ThreadStartTracer extends MethodVisitor { + private final GeneratorAdapter mv; + + ThreadStartTracer(GeneratorAdapter mv) { + super(ASM_API, mv); + this.mv = mv; + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (owner.equals("java/lang/Thread") && name.equals("start0")) { + mv.dup(); + mv.invokeStatic(METHODS_TYPE, START_THREAD_METHOD); + } + super.visitMethodInsn(opcode, owner, name, desc, itf); + } +} diff --git a/transformer/src/main/java/com/devexperts/timetest/TimeTestTransformer.java b/transformer/src/main/java/com/devexperts/timetest/transformer/TimeTestTransformer.java similarity index 77% rename from transformer/src/main/java/com/devexperts/timetest/TimeTestTransformer.java rename to transformer/src/main/java/com/devexperts/timetest/transformer/TimeTestTransformer.java index a73ce82..ffd325a 100644 --- a/transformer/src/main/java/com/devexperts/timetest/TimeTestTransformer.java +++ b/transformer/src/main/java/com/devexperts/timetest/transformer/TimeTestTransformer.java @@ -1,4 +1,4 @@ -package com.devexperts.timetest; +package com.devexperts.timetest.transformer; /* * #%L @@ -34,7 +34,6 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.JSRInlinerAdapter; -import org.objectweb.asm.commons.LocalVariablesSorter; import org.objectweb.asm.commons.TryCatchBlockSorter; import org.objectweb.asm.util.CheckClassAdapter; @@ -45,20 +44,22 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import static com.devexperts.timetest.TransformationUtils.ASM_API; +import static com.devexperts.timetest.transformer.TransformationUtils.ASM_API; -class TimeTestTransformer extends CachingClassFileTransformer { +public class TimeTestTransformer extends CachingClassFileTransformer { private final List includes; private final List excludes; private final List testClassesPatterns; + private final List nonTestClassesPatterns; private final ClassInfoCache ciCache; - TimeTestTransformer(Configuration configuration, Log log, String agentVersion) { + public TimeTestTransformer(Configuration configuration, Log log, String agentVersion) { super(log, agentVersion); this.ciCache = new ClassInfoCache(log); includes = createPatterns(configuration.include()); excludes = createPatterns(configuration.exclude()); - testClassesPatterns = createPatterns(configuration.testClasses()); + testClassesPatterns = createPatterns(configuration.testingCode()); + nonTestClassesPatterns = createPatterns(configuration.nonTestingCode()); } private List createPatterns(String[] strPatterns) { @@ -73,7 +74,7 @@ protected boolean processClass(String className, ClassLoader loader) { || className.startsWith("com/sun/") || (className.startsWith("sun/") && !className.startsWith("sun/swing/") && !className.startsWith("sun/awt/")) || className.startsWith("jdk/") - || (className.startsWith("java/") && !className.startsWith("java/util/concurrent/"))) + || (className.startsWith("java/lang") && !className.equals("java/lang/Thread"))) { return false; } @@ -89,6 +90,10 @@ private boolean inTestingCode(String className) { return testClassesPatterns.stream().anyMatch(p -> p.matcher(className).matches()); } + private boolean inNonTestingCode(String className) { + return nonTestClassesPatterns.stream().anyMatch(p -> p.matcher(className).matches()); + } + @Override public byte[] transformImpl(ClassLoader loader, final String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { @@ -99,14 +104,22 @@ public byte[] transformImpl(ClassLoader loader, final String className, Class ciCache.getOrInitClassInfoMap(loader).put(className, cInfo); ClassWriter cw = new FrameClassWriter(loader, ciCache, cInfo.getVersion()); boolean testClass = inTestingCode(className); + boolean nonTestClass = inNonTestingCode(className); + boolean testingCode = nonTestClass ? false : testClass; ClassVisitor cv = new ClassVisitor(ASM_API, cw) { @Override public MethodVisitor visitMethod(int access, String mname, String mdesc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, mname, mdesc, signature, exceptions); + if (className.equals("java/lang/Thread")) { + if (mname.equals("start")) + return new ThreadStartTracer(new GeneratorAdapter(mv, access, mname, mdesc)); + else + return mv; + } mv = new JSRInlinerAdapter(mv, access, mname, mdesc, signature, exceptions); mv = new ChangeTimeMethodsMethodTransformer(new GeneratorAdapter(mv, access, mname, mdesc)); - if (testClass && !mname.equals("") && !mname.equals("")) { - mv = new TestEnterPointsAdder(new GeneratorAdapter(mv, access, mname, mdesc)); + if ((testClass || nonTestClass) && !mname.equals("") && !mname.equals("")) { + mv = new EntryPointsAdder(testingCode, new GeneratorAdapter(mv, access, mname, mdesc)); mv = new TryCatchBlockSorter(mv, access, mname, mdesc, signature, exceptions); } return mv; diff --git a/transformer/src/main/java/com/devexperts/timetest/TransformationUtils.java b/transformer/src/main/java/com/devexperts/timetest/transformer/TransformationUtils.java similarity index 72% rename from transformer/src/main/java/com/devexperts/timetest/TransformationUtils.java rename to transformer/src/main/java/com/devexperts/timetest/transformer/TransformationUtils.java index 1b3762b..957b2a2 100644 --- a/transformer/src/main/java/com/devexperts/timetest/TransformationUtils.java +++ b/transformer/src/main/java/com/devexperts/timetest/transformer/TransformationUtils.java @@ -1,4 +1,4 @@ -package com.devexperts.timetest; +package com.devexperts.timetest.transformer; /* * #%L @@ -32,6 +32,7 @@ class TransformationUtils { static final int ASM_API = Opcodes.ASM5; static final Type METHODS_TYPE = Type.getType("com/devexperts/timetest/Methods"); static final Type OBJECT_TYPE = Type.getType(Object.class); + static final Type THREAD_TYPE = Type.getType(Thread.class); static final Method TIME_MILLIS = new Method("timeMillis", LONG_TYPE, new Type[]{}); static final Method NANO_TIME = new Method("nanoTime", LONG_TYPE, new Type[]{}); @@ -49,6 +50,11 @@ class TransformationUtils { static final Method PARK = new Method("park", VOID_TYPE, new Type[]{BOOLEAN_TYPE, LONG_TYPE}); static final Method UNPARK = new Method("unpark", VOID_TYPE, new Type[]{OBJECT_TYPE}); - static final Method ENTER_TRANSFORMED_METHOD = new Method("enterTestingCodeMethod", VOID_TYPE, new Type[]{}); - static final Method LEAVE_TRANSFORMED_METHOD = new Method("leaveTestingCodeMethod", VOID_TYPE, new Type[]{}); + static final Method IS_IN_TESTING_CODE_METHOD = new Method("isInTestingCode", BOOLEAN_TYPE, new Type[]{}); + static final Method ENTER_TESTING_CODE_METHOD = new Method("enterTestingCode", VOID_TYPE, new Type[]{BOOLEAN_TYPE}); + static final Method LEAVE_TESTING_CODE_METHOD = new Method("leaveTestingCode", VOID_TYPE, new Type[]{BOOLEAN_TYPE}); + static final Method ENTER_NON_TESTING_CODE_METHOD = new Method("enterNonTestingCode", VOID_TYPE, new Type[]{BOOLEAN_TYPE}); + static final Method LEAVE_NON_TESTING_CODE_METHOD = new Method("leaveNonTestingCode", VOID_TYPE, new Type[]{BOOLEAN_TYPE}); + + static final Method START_THREAD_METHOD = new Method("startThread", VOID_TYPE, new Type[]{THREAD_TYPE}); } \ No newline at end of file