Skip to content

Commit

Permalink
tagging release 1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikita Koval committed Feb 13, 2018
1 parent 73d126d commit f40b37b
Show file tree
Hide file tree
Showing 27 changed files with 604 additions and 203 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions agent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<artifactId>timetest</artifactId>
<groupId>com.devexperts.timetest</groupId>
<version>1.3-SNAPSHOT</version>
<version>1.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand All @@ -28,7 +28,7 @@
</goals>
<configuration>
<includeArtifactIds>asm-all,owner,owner-java8,transformer,jagent-impl</includeArtifactIds>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
<outputDirectory>${project.build.outputDirectory}/timetest</outputDirectory>
<stripVersion>true</stripVersion>
</configuration>
</execution>
Expand Down Expand Up @@ -64,13 +64,13 @@
<groupId>com.devexperts.timetest</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
<!--<scope>provided</scope> TODO -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.devexperts.timetest</groupId>
<artifactId>transformer</artifactId>
<version>${project.version}</version>
<!--<scope>provided</scope> TODO -->
<scope>provided</scope>
</dependency>
</dependencies>

Expand Down
2 changes: 1 addition & 1 deletion api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<artifactId>timetest</artifactId>
<groupId>com.devexperts.timetest</groupId>
<version>1.3-SNAPSHOT</version>
<version>1.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
79 changes: 79 additions & 0 deletions api/src/main/java/com/devexperts/timetest/DefaultTimeProvider.java
Original file line number Diff line number Diff line change
@@ -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
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
* #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);
}
}
52 changes: 22 additions & 30 deletions api/src/main/java/com/devexperts/timetest/TestTimeProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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<ThreadInfo> tis = waitingThreads.remove(monitor);
if (tis == null)
return;
tis.forEach(ti -> ti.resumed = true);
}
List<ThreadInfo> tis = waitingThreads.remove(monitor);
if (tis == null)
return;
tis.forEach(ti -> ti.resumed = true);
}

@Override
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
Expand Down
91 changes: 7 additions & 84 deletions api/src/main/java/com/devexperts/timetest/TimeProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Integer> testMethodsCallStackSize = new ThreadLocal<Integer>() {
@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<Boolean> inTestingCode = ThreadLocal.withInitial(() -> false);

/**
* Resets time provider to {@link #DEFAULT default}. Should be used for test purpose only.
Expand All @@ -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;
}

/**
Expand All @@ -139,6 +60,8 @@ static void setTimeProvider(TimeProvider timeProvider) {
TimeProvider.timeProvider = timeProvider;
}

// ========== Time-based methods ==========

/**
* @see System#currentTimeMillis()
*/
Expand Down
6 changes: 1 addition & 5 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@
<parent>
<artifactId>timetest</artifactId>
<groupId>com.devexperts.timetest</groupId>
<version>1.3-SNAPSHOT</version>
<version>1.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>core</artifactId>

<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>

<build>
<!-- Processes resources directory to replace Maven's variables to theirs values -->
<resources>
Expand Down
Loading

0 comments on commit f40b37b

Please sign in to comment.