Skip to content

zhishengzhang/concurrentunit

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

72 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ConcurrentUnit Build Status

A simple, zero-dependency tool for testing multi-threaded code.

Introduction

ConcurrentUnit allows you to write tests capable of performing assertions or waiting for expected operations across multiple threads, with failures being properly reported back to the main test thread. If an assertion fails, your test fails, regardless of which thread the assertion came from.

Setup

Add ConcurrentUnit as a Maven dependency:

<dependency>
  <groupId>net.jodah</groupId>
  <artifactId>concurrentunit</artifactId>
  <version>0.3.3</version>
</dependency>

Usage

  • Create a Waiter
  • Use waiter.await to block the main test thread while waiting for other threads to perform assertions.
  • Use the waiter.assert calls from any thread to perform assertions.
  • Once expected assertions are completed, use waiter.resume call to unblock the main thread.

Optional:

  • Use waiter.expectResumes to indicate the number of resume calls the waiter should expect. This is useful when resume may be called by some thread prior to await.

Assertion failures will result in the main thread being interrupted and the failure thrown. If a blocking operation times out before all expected waiter.resume calls occur, the test is failed with a TimeoutException.

Examples

Perform an assertion from a worker thread while blocking the main thread until resume is called:

@Test
public void shouldWaitForResume() throws Throwable {
  final Waiter waiter = new Waiter();

  // Start worker thread that performs an assertion after some delay, then resumes the waiter
  new Thread(new Runnable() {
    public void run() {
      doSomeWork();
      waiter.assertTrue(true);
      waiter.resume();
    }
  }).start();
  
  // Waits for resume to be called
  waiter.await(1000);
}

Multiple threads can be used along with any number of expected resume calls:

@Test
public void shouldWaitForResumes() throws Throwable {
  final Waiter waiter = new Waiter();
  int expectedResumes = 5;
  waiter.expectResumes(expectedResumes);

  for (int i = 0; i < expectedResumes; i++) {
    new Thread(new Runnable() {
      public void run() {
        waiter.assertTrue(true);
        waiter.resume();
      }
    }).start();
  }
  
  waiter.await(1000);
}

Failed assertions from a worker thread are thrown by the main test thread as expected:

@Test(expected = AssertionError.class)
public void shouldFail() throws Throwable {
  final Waiter waiter = new Waiter();

  new Thread(new Runnable() {
    public void run() {
      delayFor(100);
      waiter.assertTrue(false);
    }
  }).start();
  
  waiter.await();
}

TimeoutException is thrown if resume is not called before the await time is exceeded:

@Test(expected = TimeoutException.class)
public void shouldTimeout() throws Throwable {
  new Waiter().await(1);
}

Alternatively

As a more concise alternative to using the Waiter class, you can extend the ConcurrentTestCase:

class SomeTest extends ConcurrentTestCase {
	@Test
	public void shouldSucceed() throws Throwable {
	  new Thread(new Runnable() {
	    public void run() {
	      delayFor(100);
	      threadAssertTrue(true);
	      resume();
	    }
	  }).start();
	  
	  await(1000);
	}
}

Other Examples

More examples can be found in the WaiterTest or in the following projects:

Docs

JavaDocs are available here.

License

Copyright 2010-2014 Jonathan Halterman - Released under the Apache 2.0 license.

About

Toolkit for testing multi-threaded code

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 98.6%
  • Shell 1.4%