Skip to content

Commit

Permalink
timetest-1.2
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikita Koval committed Apr 24, 2017
1 parent d6542ed commit 73d126d
Show file tree
Hide file tree
Showing 22 changed files with 970 additions and 617 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

1.2 - 2017-04-24
----------------
* Fix deadlock in TestTimeProvider
* Support "unpark before park" executions

1.1 - 2016-10-27
----------------
* Support class caching
Expand All @@ -14,4 +19,4 @@ Changelog

0.9 - 2015-10-13
----------------
* Initial public version
* Initial public version
97 changes: 47 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,67 @@
time-test
=========
# time-test

[ ![Download](https://api.bintray.com/packages/devexperts/Maven/time-test/images/download.svg) ](https://bintray.com/devexperts/Maven/time-test/_latestVersion)

**time-test** -- framework for testing time-based functionality.
**time-test** is a framework for testing time-based functionality.

Sometimes you have code with similar logic:
"wait for 30 seconds and then do something if no changes are detected".
"wait for 30 seconds and then do something if no changes have been detected".

If you want to test such logic you should write something like this:
If you want to test such logic you can write something like this:

```
doWork();
Thread.sleep(30_000);
Thread.sleep(30_000 + 200);
check();
```

But what if you want to test logic which should be executed once per month? Or only on specific days?
However, such tests are undesirable to be in the project. Also what if you want to test logic which should be executed once per month? Or what if this "something doing" hasn't done before the check starts?

**time-test** provides easy way to test such logic. For example, the code above can be written else:
**time-test** provides an easy way to test such logic. For example, the code above can be written more clear:

```
doWork();
TestTimeProvider.increaseTime(30_000);
TestTimeProvider.waitUntilThreadsAreFrozen(200 /*ms*/);
check();
```

TimeProvider
------------
This interface provides implementation of time-based methods.
And this test doesn't do superfluous work.

**time-test** instruments byte-code and change time-based methods invocations (such as System.currentTimeMillis, Object.wait, Unsafe.park) to our own implementation.


# TimeProvider
This interface provides an implementation of time-based methods.
The default implementation forwards all calls to standard system methods.

Use `XXXTimeProvider.start()` to start using it.
Don't forget to reset TimeProvider to default.
Use `XXXTimeProvider.reset()` for this.

### Transformation rules
* `System.currentTimeMillis` -> `TimeProvider.timeMillis`
* `Object.nanoTime` -> `TimeProvider.nanoTime`
* `Object.wait` -> `TimeProvider.waitOn`
* `Thread.sleep` -> `TimeProvider.sleep`
* `Unsafe.park` -> `TimeProvider.park`
* `Unsafe.unpark` -> `TimeProvider.unpark`

### DummyTimeProvider
## DummyTimeProvider
**DummyTimeProvider** throws `UnsupportedOperationException` on all method calls.
It should be used for testing functionality which does not depend on time.

### TestTimeProvider
## TestTimeProvider
**TestTimeProvider** provides full access on time.
Use `TestTimeProvider.setTime(millis)` and `TestTimeProvider.inscreaseTime(millis)`
to change current time.
Use `TestTimeProvider.waitUntilThreadsAreFrozen` to wait until all threads complete their work.


Confguration
------------
You can pass your own configuration in `timetest.properties` properties file.
This file should be in classpath. Also you can pass properties to command line.
# 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.

Configuration has 2 properties:
* **include** - list of patterns in glob format, if any of this pattern applies
processed class name then this class is transformed.
* **exclude** - list of patterns in glob format, if any of this pattern not applies
processed class name then this class is transformed. Overrides **include** property.
* **timetest.tests** - defines test entrance classes in glob format. Usually, test classes. Default value: *\*Test* (all classes with suffix *Test*)
* ***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.

### Default configuration:
```xml
exclude = java.util.logging.*,\
org.apache.log4j.*,\
org.apache.logging.*,\
org.slf4j.*,\
com.devexperts.logging.*,\
org.junit.*,\
org.apache.maven.*
```

Maven
-----
Timetest is implemented as java agent and you should copy it to build directory
# Maven
**Time-test** is implemented as java agent and you should copy it to build directory
and configure *maven-surefire-plugin* to use it for tests.

```xml
Expand All @@ -89,7 +72,7 @@ and configure *maven-surefire-plugin* to use it for tests.
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-sample-agent</id>
<id>copy-timetest-agent</id>
<phase>process-test-classes</phase>
<goals>
<goal>copy</goal>
Expand Down Expand Up @@ -119,17 +102,20 @@ and configure *maven-surefire-plugin* to use it for tests.
...
```

Example
-------
# Usage example
```java
@Before
public void setUp() {
TestTimeProvider.start();
}

@After
public void tearDown() {
TestTimeProvider.reset();
}

@Test(timeout = 100)
public void testSleepWithTestTimeProvider() {
TestTimeProvider.start();
Thread t = new Thread(() -> {
// Do smth
int sum = 0;
Expand All @@ -143,4 +129,15 @@ public void testSleepWithTestTimeProvider() {
TestTimeProvider.increaseTime(10_000);
t.join();
}
```
```


# Contacts
If you need help, you have a question, or you need further details on how to use **time-test**, you can refer to the following resources:

* [dxLab](https://code.devexperts.com/) research group at Devexperts
* [GitHub issues](https://github.com/Devexperts/lin-check/issues)

You can use the following e-mail to contact us directly:

![](dxlab-mail.png)
4 changes: 2 additions & 2 deletions agent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@
<groupId>com.devexperts.timetest</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
<!--<scope>provided</scope> TODO -->
</dependency>
<dependency>
<groupId>com.devexperts.timetest</groupId>
<artifactId>transformer</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
<!--<scope>provided</scope> TODO -->
</dependency>
</dependencies>

Expand Down
28 changes: 24 additions & 4 deletions api/src/main/java/com/devexperts/timetest/DummyTimeProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*/


import com.devexperts.util.UnsafeHolder;

/**
* Dummy implementation of {@link TimeProvider}. Throws {@link UnsupportedOperationException}
* on all method calls. It is recommended to use this {@code time provider} for tests as default.
Expand Down Expand Up @@ -69,22 +71,40 @@ public void sleep(long millis, int nanos) throws InterruptedException {
}

@Override
public void waitOn(Object monitor, long millis) {
public void waitOn(Object monitor, long millis) throws InterruptedException {
throw new UnsupportedOperationException();
}

@Override
public void waitOn(Object monitor, long millis, int nanos) throws InterruptedException {
throw new UnsupportedOperationException();
if (millis == 0 && nanos == 0) {
monitor.wait();
} else {
throw new UnsupportedOperationException();
}
}

@Override
public void notify(Object monitor) {
monitor.notify();
}

@Override
public void notifyAll(Object monitor) {
monitor.notifyAll();
}

@Override
public void park(boolean isAbsolute, long time) {
throw new UnsupportedOperationException();
if (!isAbsolute && time == 0) {
UnsafeHolder.UNSAFE.park(false, 0);
} else {
throw new UnsupportedOperationException();
}
}

@Override
public void unpark(Object thread) {
throw new UnsupportedOperationException();
UnsafeHolder.UNSAFE.unpark(thread);
}
}
Loading

0 comments on commit 73d126d

Please sign in to comment.