Generating Unit Tests for Concurrent Classes

Generating Unit Tests for Concurrent Classes

Generating Unit Tests for Concurrent Classes Sebastian Steenbuck Gordon Fraser Saarland University – Computer Science University of Sheffield Saarbrucken,¨ Germany Sheffield, UK [email protected] gordon.fraser@sheffield.ac.uk Abstract—As computers become more and more powerful, 1 public Set entrySet() { programs are increasingly split up into multiple threads to 2 Set result = entrySet; leverage the power of multi-core CPUs. However, writing cor- 3 return (entrySet == null) ? rect multi-threaded code is a hard problem, as the programmer 4 entrySet = new AsMapEntries() has to ensure that all access to shared data is coordinated. : Existing automated testing tools for multi-threaded code mainly 5 result; focus on re-executing existing test cases with different sched- 6 } ules. In this paper, we introduce a novel coverage criterion Figure 1. Concurrency bug #339 in the HashMultimap.AsMap class that enforces concurrent execution of combinations of shared in the Guava library: entrySet() may return null if two threads call memory access points with different schedules, and present the method at the same time. an approach that automatically generates test cases for this HashMultimap multi0 = new HashMultimap(); coverage criterion. Our CONSUITE prototype demonstrates HashMultimap.AsMap map0 = m.asMap(); that this approach can reliably reproduce known concurrency errors, and evaluation on nine complex open source classes Thread 1: Thread 2: revealed three previously unknown data-races. map0.entrySet(); map0.entrySet(); Keywords-concurrency coverage; search based software en- Figure 2. Concurrent unit test produced by CONSUITE reveal- gineering; unit testing ing the bug in Figure1: Two threads independently access a shared HashMultimap.AsMap instance, and are executed with all interleavings of the three accesses to the entrySet field in Figure1. I. INTRODUCTION The increasing use of multi-core processors makes it ever behavior: If entrySet is null initially, then the first more important to use multi-threaded programming to lever- thread assigns null to result and should then check if age the power of multiple cores. However, writing correct entrySet was null. However, if the scheduler switches multi-threaded code is a hard problem, as the programmer the context before the check can happen, the second thread has to ensure that all access to shared data is coordinated. may assign a value to entrySet. The first thread will The coordination is usually done with some sort of locking – evaluate the condition to false and return its local result which in turn might lead to deadlocks [20]. Software testing variable, which is null. is an important countermeasure to identify such concurrency Automated test generation is important to simplify soft- issues in programs. ware testing, and modern test generation techniques can Writing test cases for concurrency errors is problematic efficiently generate test data that exercise program code for three reasons: (1) The scheduler might not be control- thoroughly. However, structural test generation approaches lable by the programmer; this is the case in Java. (2) The often assume that there is no concurrency (e.g., [9]). On programmer might not fully understand the consequences the other hand, tools to automate concurrent testing often of multiple threads accessing the same memory. (3) The assume that tests already exist such that different schedules programmer might not think of some particular schedule that can be explored (e.g., [7], [18]). would be required to lead to a concurrency issue; e.g., he In this paper we present a technique that generates both, might know about a data race but assumes that one thread concurrent test cases and execution schedules. All the tech- always needs much longer than another thread and therefore nique needs as input is the bytecode of a class under test does not enforce the order of the threads [16]. (CUT). It generates test cases such that combinations of For example, Figure1 shows the entrySet method of memory accesses are exercised by different threads, and the HashMultimap.AsMap class, containing bug #3391. then explores different schedules for these combinations. If two threads access the same hashmap and call entrySet For example, Figure1 contains three synchronization points at the same time, a race condition can lead to erroneous (memory accesses to a shared variable, definition in Sec- tion II-C) when accessing the entryMap member variable 1http://code.google.com/p/guava-libraries/issues/detail?id=339 at Lines2,3, and4. Our approach would create test cases to cover all pairs and triples of such synchronization points, up this exploration (e.g., [5]). Finding good schedules can and would then execute these with all possible interleavings. also be interpreted as an optimization problem [8] solvable, Figure2 shows such an example test case, and this test case for example, by a GA. LineUp [3] checks linearizability easily reveals the data race, leading to a null return value. by comparing whether sequential behavior to interleaved In detail, the contributions of this paper are as follows: behavior, but assumes a set of method calls given as input, • Concurrency coverage: We formalize a coverage cri- i.e., it does not address the test generation problem. terion based on observations on real concurrency bugs There are only few approaches that try to generate not (Section II). only schedules, but also test cases: Krena et al [13] proposed • Test generation: We present a search-based technique a search-based technique that optimizes schedules used by to derive concurrent test cases that exercise a shared ob- the Contest [7] tool. Ballerina [19] uses random test prefixes, ject with respect to the concurrency coverage criterion and then explores two methods executed in parallel in two (Section III). different threads. Sen and Agha [22] combined jCute [23] • Evaluation: We demonstrate on a set of concurrency with race detection algorithms. The Ballerina tool [19] is benchmarks that the approach can reliably reproduce the most similar to the approach described in this paper: known concurrency issues (Section IV). It randomly invokes methods on a shared object under test • Real bugs: We show the results of an evaluation on a in parallel. However, it cannot find concurrency issues that set of open source classes, revealing several previously require execution of parallel sequences of code rather than unknown data races (Section IV). individual method calls, and it cannot find data races based on more than two memory access points [16] (e.g., Figure4). II. BACKGROUND Godefroid and Khurshid [11] used a GA in a model check- A. Evolutionary Testing of Classes ing approach to find deadlocks; their fitness function aims to Search-based testing applies meta-heuristic search tech- maximize the number of waiting threads. A similar approach niques to test data generation [17]. A popular algorithm is also followed by Alba et al [1]. These approaches search also used in this paper is a ge¡netic algorithm (GA), in for individual program inputs rather than sequences of calls, which a population of candidate solutions is evolved towards and they are not driven by structural coverage criteria. In satisfying a chosen coverage criteria. A fitness function model checking partial order reduction [10] is used to reduce guides the search in choosing individuals for reproduction, the number of schedules that need to be explored. In prin- gradually improving the fitness values with each generation ciple, partial order reduction could serve as an optimization until a solution is found. of our test generation approach. In this paper we consider object oriented software, for C. Concurrency Coverage which tests are essentially small programs exercising the classes under test. A common representation is a variable Systematic test generation is often guided by a cover- length instruction sequence [9], [26] (method sequences). age criterion that describes which aspects of a program Each entry in such a sequence represents one statement a test suite needs to cover; e.g., statements or branches. in the code, such as calls to constructors, methods, or To explore different interesting concurrency scenarios, dedi- assignments to variables, member fields, or arrays. Recently, cated coverage criteria for concurrency have been proposed. search-based testing has also been extended to whole test Synchronization coverage [2] was designed as an easy to suite generation [9], where test suites are evolved with understand concurrent coverage criterion such that testers similar operators towards satisfying entire coverage criteria. would be able to apply it when manually writing unit tests. The coverage criterion requires that for each synchronization B. Concurrent Testing statement in the program (e.g., synchronize blocks, wait(), The majority of work on automated unit testing considers notify(), notifyAll() of an object instance and others) there the case of single-threaded programs. In the context of is a test such that a thread is stopped from progressing at multi-threaded programs, test automation usually focuses on this statement. the exploration of different schedules for existing program To also consider shared data accesses that are not guarded runs. For example, the DejaVu platform [4] allows the by synchronization statements, Lu et al [15] defined a hi- deterministic replay of a multi-threaded Java program. Other erarchy of more rigorous criteria, including all-interleaving approaches assume existing tests such that different sched- coverage, which requires that all feasible interleavings of ules for these tests can be tested. For example, Contest [7] shared access from all threads are covered; thread pair allows the replay of program runs and the generation of interleaving requires for a pair of threads that all feasible in- new interleavings by adding random noise to the scheduler. terleavings are covered; single variable interleaving requires Alternatively, stateless model checkers like CHESS [18] that for one variable all accesses from any pair of threads are can systematically explore all thread interleavings for a covered; finally, partial interleaving requires either coverage given test case.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    10 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us