Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Unit-testing
concurrent code
public class FlawedList<T> extends ArrayList<T> {
public boolean putIfAbsent(T object) {
boolean absent = !super.contains(...
@Test
public void testPutIfAbsent() {
FlawedList<String> list = new FlawedList<String>();
list.putIfAbsent("foo");
list.pu...
FlawedList<String> list = new FlawedList<String>();
@Test(threadPoolSize = 5, invocationCount = 20)
public void testList()...
public class FlawedList<T> extends ArrayList<T> {
public boolean putIfAbsent(T object) {
boolean absent = !super.contains(...
public boolean putIfAbsent(T object) {
boolean absent = !super.contains(object);
if (absent) {
super.add(object);
}
return...
ThreadWeaver
public class WeavedFlawedListTest {
private FlawedList<String> list;
@ThreadedBefore public void before() {
l...
public class MyListTest {
@Test
public void testFlawedList() {
AnnotatedTestRunner runner = new AnnotatedTestRunner();
run...
@Test
public void testFlawedList() {
AnnotatedTestRunner runner = new AnnotatedTestRunner();
runner.runTests(getClass(), F...
Beware of the Java memory model!
Framework.considerBreakpoint(Thread.currentThread(), 1);
Any breakpoint relies on synchro...
Define two actors that execute potentially conflicting actions. Define legal,
suspicious and illegal outcomes of this inte...
http://rafael.codes
@rafaelcodes
http://documents4j.com
https://github.com/documents4j/documents4j
http://bytebuddy.net
ht...
Upcoming SlideShare
Loading in …5
×

Unit testing concurrent code

While software engineers can disagree on almost any concept of programming best-practice, the necessity of writing unit tests remains undisputed. With the advent of concurrent applications and the ongoing deprecation of the one-thread-per-request model, unit tests do however miss an increasing fraction of programming errors such as race conditions or dead-locking code. But is it even possible to write tests that revise such errors? In the end, a good unit test is characterized by a determined execution path what effectively prevents the use of concurrency within a single test. However, there are tools and programming principles that allow for unit tests of concurrent code. This talk reviews typical mistakes made when concurrent code is tested and introduces Thread Weaver, a test suite for writing valid unit tests that uncover concurrency-related programming errors.

  • Be the first to comment

Unit testing concurrent code

  1. 1. Unit-testing concurrent code
  2. 2. public class FlawedList<T> extends ArrayList<T> { public boolean putIfAbsent(T object) { boolean absent = !super.contains(object); if (absent) { super.add(object); } return absent; } } Implementing a (flawed) unique list
  3. 3. @Test public void testPutIfAbsent() { FlawedList<String> list = new FlawedList<String>(); list.putIfAbsent("foo"); list.putIfAbsent("foo"); assertThat(list.size(), is(1)); } JUnit
  4. 4. FlawedList<String> list = new FlawedList<String>(); @Test(threadPoolSize = 5, invocationCount = 20) public void testList() { list.putIfAbsent("foo"); assertThat(list.size(), is(1)); } TestNG
  5. 5. public class FlawedList<T> extends ArrayList<T> { public boolean putIfAbsent(T object) { boolean absent = !super.contains(object); if (absent) { super.add(object); } return absent; } } FlawedList<String> list = new FlawedList<String>(); @Test(threadPoolSize = 5, invocationCount = 20) public void testList() { list.putIfAbsent("foo"); assertThat(list.size(), is(1)); } TestNG with breakpoint
  6. 6. public boolean putIfAbsent(T object) { boolean absent = !super.contains(object); if (absent) { super.add(object); } return absent; } Testing with break points: first weaving FlawedList["foo", "foo"]FlawedList["foo"]FlawedList[] Testing with break points: second weaving Thread 1 Thread 2 Legend: absent true falsetrue ThreadWeaver
  7. 7. ThreadWeaver public class WeavedFlawedListTest { private FlawedList<String> list; @ThreadedBefore public void before() { list = new FlawedList<String>(); } @ThreadedMain public void mainThread() { list.putIfAbsent("foo"); } @ThreadedSecondary public void secondThread() { list.putIfAbsent("foo"); } @ThreadedAfter public void after() { assertEquals(1, list.size()); } }
  8. 8. public class MyListTest { @Test public void testFlawedList() { AnnotatedTestRunner runner = new AnnotatedTestRunner(); runner.runTests(getClass(), FlawedList.class); } // put method with @Threaded<...> annotations here } Seamless test-suite integration
  9. 9. @Test public void testFlawedList() { AnnotatedTestRunner runner = new AnnotatedTestRunner(); runner.runTests(getClass(), FlawedList.class); } Instrumented code: public boolean putIfAbsent(T object) { Framework.considerBreakpoint(Thread.currentThread(), 0); boolean absent = !super.contains(object); Framework.considerBreakpoint(Thread.currentThread(), 1); if (absent) { Framework.considerBreakpoint(Thread.currentThread(), 2); super.add(object); } Framework.considerBreakpoint(Thread.currentThread(), 3); return absent; } How does it work?
  10. 10. Beware of the Java memory model! Framework.considerBreakpoint(Thread.currentThread(), 1); Any breakpoint relies on synchronized code blocks which trigger a memory synchronization and a “happens-before” relationship. The tested code is therefore executed sequentially consistent. @JCStressTest @State @Outcome(id = "{0}", expect = Expect.ACCEPTABLE) @Outcome(id = "{1}", expect = Expect.ACCEPTABLE) @Outcome(id = "{2}", expect = Expect.FORBIDDEN) class FlawedListStressTest { final FlawedList list = new FlawedList(); @Actor void actor1() { list.putIfAbsent("foo"); } @Actor void actor2() { list.putIfAbsent("foo"); } @Arbiter void observe(IntResult1 result) { result.r1 = list.size(); } } jcstress: concurrency stress testing concurrent code
  11. 11. Define two actors that execute potentially conflicting actions. Define legal, suspicious and illegal outcomes of this interaction. jcstress executes these actions repeatedly to maybe trigger concurrency bugs. jcstress: concurrency stress testing concurrent code (2) Not a unit test! Results might trigger, but it will likely vary from run to run. Results might depend on the processor architecture. For example, ARM and x86 have significantly different semantics on memory access reordering, for Example ARM might reorder reads while x86 does never apply such reorderings.
  12. 12. http://rafael.codes @rafaelcodes http://documents4j.com https://github.com/documents4j/documents4j http://bytebuddy.net https://github.com/raphw/byte-buddy

    Be the first to comment

    Login to see the comments

  • MaktumNadaf

    Sep. 13, 2014
  • tycoi2005

    Apr. 9, 2015
  • GuangWanLiao

    Apr. 17, 2015

While software engineers can disagree on almost any concept of programming best-practice, the necessity of writing unit tests remains undisputed. With the advent of concurrent applications and the ongoing deprecation of the one-thread-per-request model, unit tests do however miss an increasing fraction of programming errors such as race conditions or dead-locking code. But is it even possible to write tests that revise such errors? In the end, a good unit test is characterized by a determined execution path what effectively prevents the use of concurrency within a single test. However, there are tools and programming principles that allow for unit tests of concurrent code. This talk reviews typical mistakes made when concurrent code is tested and introduces Thread Weaver, a test suite for writing valid unit tests that uncover concurrency-related programming errors.

Views

Total views

2,793

On Slideshare

0

From embeds

0

Number of embeds

46

Actions

Downloads

44

Shares

0

Comments

0

Likes

3

×