Thursday, April 11, 2013

Check that your code is thread-safe with JUnit and ContiPerf

This short article shows how to check that you write thread-safe code by transforming a JUnit test into a concurrent one with ContiPerf.

Suppose that you have to test a date formatter component that uses the famous SimpleDateFormat class, as illustrated below:
package org.javabenchmark;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Helper dedicated to format date in a standard way.
 */
public class NonThreadSafeDateFormatHelper {

    /**
     * the date format for standard representation.
     */
    private SimpleDateFormat standardDateFormat = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * formats the given date using the standard date format: yyyy-MM-dd.
     *
     * @param date the date to format
     * @return a literal representation of the given date.
     */
    public String toStandardString(Date date) {
        return standardDateFormat.format(date);
    }
}

Then, you could write a junit test like this:
package org.javabenchmark;

import org.junit.Test;

import java.util.Calendar;

import static org.fest.assertions.api.Assertions.*;

public class NonThreadSafeDateFormatHelperTest {

    @Test
    public void shouldFormatRandomDate() {

        // random date
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, (int) (1000 + Math.random() * 1000));
        calendar.set(Calendar.DAY_OF_YEAR, (int) (Math.random() * 365));

        // test
        NonThreadSafeDateFormatHelper dateFormatHelperToTest = new NonThreadSafeDateFormatHelper();
        String randomDateString = dateFormatHelperToTest.toStandardString(calendar.getTime());

        // general controls
        assertThat(randomDateString).isNotNull();
        assertThat(randomDateString).hasSize(10);
        // year control
        String literalYear = String.valueOf(calendar.get(Calendar.YEAR));
        assertThat(literalYear).isEqualTo(randomDateString.substring(0, 4));
        // month control
        String literalMonth = String.valueOf(calendar.get(Calendar.MONTH) + 1);
        if (literalMonth.length() == 1) {
            literalMonth = "0" + literalMonth;
        }
        assertThat(literalMonth).isEqualTo(randomDateString.substring(5, 7));
        // day control
        String literalDayh = String.valueOf(calendar.get(Calendar.DAY_OF_MONTH));
        if (literalDayh.length() == 1) {
            literalDayh = "0" + literalDayh;
        }
        assertThat(literalDayh).isEqualTo(randomDateString.substring(8));
    }
}

Next, running the test will produce the following output:
Testsuite: org.javabenchmark.NonThreadSafeDateFormatHelperTest
Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.173 sec

The test passes, and you could think that everything is fine, but what will happen if the DateFormatHelper class is used in a concurrent way, for instance from a JSF page to display the current date ?

To check that your code can handle concurrency, you can modify the previous JUnit test like this:
package org.javabenchmark;

import org.junit.Rule;
import org.junit.Test;

import java.text.ParseException;
import java.util.Calendar;
import org.databene.contiperf.PerfTest;
import org.databene.contiperf.junit.ContiPerfRule;

import static org.fest.assertions.api.Assertions.assertThat;

public class NonThreadSafeDateFormatHelperPerfTest {

    @Rule
    public ContiPerfRule i = new ContiPerfRule();
    /**
     * the date format helper to test.
     */
    private NonThreadSafeDateFormatHelper dateFormatHelperToTest = new NonThreadSafeDateFormatHelper();

    @Test
    @PerfTest(invocations = 1000, threads = 2)
    public void shouldFormatRandomDatesConcurrently() throws ParseException {

        // random date
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, (int) (1000 + Math.random() * 1013));
        calendar.set(Calendar.DAY_OF_YEAR, (int) (Math.random() * 365));
        
        // test
        String randomDateString = dateFormatHelperToTest.toStandardString(calendar.getTime());

        // general controls
        assertThat(randomDateString).isNotNull();
        assertThat(randomDateString).hasSize(10);
        // year control
        String literalYear = String.valueOf(calendar.get(Calendar.YEAR));
        assertThat(literalYear).isEqualTo(randomDateString.substring(0, 4));
        // month control
        String literalMonth = String.valueOf(calendar.get(Calendar.MONTH) + 1);
        if (literalMonth.length() == 1) {
            literalMonth = "0" + literalMonth;
        }
        assertThat(literalMonth).isEqualTo(randomDateString.substring(5, 7));
        // day control
        String literalDayh = String.valueOf(calendar.get(Calendar.DAY_OF_MONTH));
        if (literalDayh.length() == 1) {
            literalDayh = "0" + literalDayh;
        }
        assertThat(literalDayh).isEqualTo(randomDateString.substring(8));
    }
}

This is the same test, except that the test method is invoked 1000 times by 2 threads, producing this output:
Testsuite: org.javabenchmark.NonThreadSafeDateFormatHelperPerfTest
org.javabenchmark.NonThreadSafeDateFormatHelperPerfTest.shouldFormatRandomDatesConcurrently
samples: 999
max:     19
average: 0.04804804804804805
median:  0
Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0.377 sec
So, there is now an error, indicating that the DateFormatHelper component is not thread-safe: do not let it go into production :)

Summary

Good unit tests are not sufficient when you are writing components that will evolve in multi-threaded environment, like web applications. You can easily check if your code is vulnerable to race condition with contiperf.

20 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. This is a cool idea, and I like the syntax a lot.

    The problem from my point of view: If you are smart enough to use a "is my code threadsafe test", you likely are smart enough to already write threadsafe code :)

    Plus the fact that some race conditions may only happen 1 time in 10 years.. you would need to run this test for a really long time to weed out all of your race conditions ;) Much better to just read up on JCIP and try to make thread safe code...

    ReplyDelete
    Replies
    1. Hi Brian, thanks for the comment. I agree with you but sometimes you test code that relies on customer framework, or third-party libraries. With this approach, you can spot concurrency issues that are hidden in the dependencies that your code is using without exploring source or javadoc to be sure that all component are thread-safe.

      Delete
  3. I agree with Brian,
    the title of this post could be "Check that your code is probably thread-safe with JUnit and ContiPerf".
    Regards.

    ReplyDelete
  4. Thanks! Does it work with TestNG?

    ReplyDelete
    Replies
    1. As far as i know, ContiPerf is designed for JUnit only.

      Delete
  5. Hi,

    I developed a tool called http://vmlens.com which can detect such bugs. It searches for memory fields which are accessed from different threads without synchronization. It will detect such bugs when at least two threads accessing a field without synchronization.

    Regards
    Thomas

    ReplyDelete
  6. The vast majority of the activity procedure is conveyed on the web, and you show hit exercises which penury to be figured it out. Java

    ReplyDelete
  7. I'm getting the following error:
    java.lang.RuntimeException: java.lang.NoSuchFieldException: fNext

    at org.databene.contiperf.junit.ContiPerfRule.apply(ContiPerfRule.java:176)
    at org.junit.runners.BlockJUnit4ClassRunner.withMethodRules(BlockJUnit4ClassRunner.java:365)
    at org.junit.runners.BlockJUnit4ClassRunner.withRules(BlockJUnit4ClassRunner.java:355)
    at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:278)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    Caused by: java.lang.NoSuchFieldException: fNext
    at java.lang.Class.getDeclaredField(Class.java:2070)
    at org.databene.contiperf.junit.ContiPerfRule.apply(ContiPerfRule.java:171)

    Is it because I've addeded lombok as provided dependency?

    ReplyDelete
  8. Nice Info my sincere thanks for sharing this post Please continue to share this post
    Selenium Training in Bangalore
    Selenium Training in BTM Layout

    ReplyDelete
  9. Threads come in different sizes however their accessibility is low so they are for the most part utilized for extravagance textures.best machine embroidery thread brand

    ReplyDelete
  10. https://perfspy.blogspot.in/2016/02/one-thing-to-rule-them-all-ansible-on.html?showComment=1526543672568#c8554519181269916181

    ReplyDelete
  11. Data science Training Institute in Noida

    Webtrackker Data science Training Institute in Noida Accelerate your career in data science by starting from basics in Statistics, Data Management and Analytics to advanced topics like Neural Networks, Machine Learning and Big Data.



    http://webtrackker.com/Best-Data-Science-Training-Institute-in-Noida.php



    Data science Training Institute in Noida

    ReplyDelete
  12. Artificial intelligence Training in noida
    Artificial intelligence Training in noida-Artificial Intelligence Training in Noida, Artificial Intelligence Training classes in Noida, Artificial Intelligence Training classes in Noida, Artificial Intelligence Training

    by Real time ARTIFICIAL INTELLIGENCE Experts, Big-Data and ARTIFICIAL INTELLIGENCE Certification Training in Noida



    WEBTRACKKER TECHNOLOGY (P) LTD.
    C - 67, sector- 63, Noida, India.
    F -1 Sector 3 (Near Sector 16 metro station) Noida, India.

    +91 - 8802820025
    0120-433-0760
    0120-4204716
    EMAIL: info@webtrackker.com
    Website: www.webtrackker.com



    Our Other Courses:


    artificial intelligence Training in noida

    SAS Training Institute in Delhi

    SAS Training in Delhi

    SAS Training center in Delhi

    Sap Training Institute in delhi

    Sap Training in delhi

    Best Sap Training center in delhi

    Best Software Testing Training Institute in delhi

    Software Testing Training in delhi

    Software Testing Training center in delhi

    Best Salesforce Training Institute in delhi

    Salesforce Training in delhi

    Salesforce Training center in delhi

    Best Python Training Institute in delhi



    Python Training in delhi


    Best Android Training Institute In delhi


    Best Python Training center in delhi


    Android Training In delhi


    best Android Training center In delhi

    ReplyDelete

  13. Best Solidworks training institute in noida

    SolidWorks is a solid modeling computer-aided design (CAD) and computer-aided engineering (CAE) computer program that runs on Microsoft Windows. SolidWorks is published by Dassault Systems. Solid Works: well, it is purely a product to design machines. But, of course, there are other applications, like aerospace, automobile, consumer products, etc. Much user friendly than the former one, in terms of modeling, editing designs, creating mechanisms, etc.
    Solid Works is a Middle level, Main stream software with focus on Product development & this software is aimed at Small scale & Middle level Companies whose interest is to have a reasonably priced CAD system which can support their product development needs and at the same time helps them get their product market faster.

    Company Address:

    WEBTRACKKER TECHNOLOGY (P) LTD.
    C-67,Sector-63,Noida,India.

    E-mail: info@webtracker.com

    Phone No: 0120-4330760 ,+91-880-282-0025


    http://webtrackker.com/solidworks-training-Course-institute-in-noida-delhi.php

    Best Solidworks training institute in noida

    ReplyDelete
  14. 3D Animation and Multimedia Training in Noida
    Best institute for 3d Animation and Multimedia Course training Classes in Noida- webtrackker Is providing the 3d Animation and
    Multimedia training in noida with 100% placement supports. for more call - 8802820025.
    3D Animation and Multimedia Training in Noida
    Company Address:
    Webtrackker Technology
    C- 67, Sector- 63, Noida
    Phone: 01204330760, 8802820025
    Email: info@webtrackker.com
    Website: http://webtrackker.com/Best-institute-3dAnimation-Multimedia-Course-training-Classes-in-Noida.php

    ReplyDelete
  15. Graphics designing training institute in Noida
    Best Graphics training institute in Noida, Graphic Designing Course, classes in Noida- webtrackker is providing the graphics training in Noida with 100% placement supports. If you are looking for the Best Graphics designing training institute in Noida For more call - 8802820025.

    Graphics designing training institute in Noida, Graphics designing training in Noida, Graphics designing course in Noida, Graphics designing training center in Noida

    Company address:
    Webtrackker Technology
    C- 67, Sector- 63, Noida
    Phone: 01204330760, 8802820025
    Email: info@webtrackker.com
    Website: http://webtrackker.com/Best-institute-for-Graphic-Designing-training-course-in-noida.php

    ReplyDelete