Abstract:
A system and method are disclosed for determining the root cause of non-deterministic tests. An exemplary system may receive a non-deterministic test and run the non-deterministic test with call trace collection enabled. For each trace, the system may determine whether the trace was associated with a passing or failing test. Each trace may be labeled according to the trace&#39;s association. Then, dynamic call trees may be constructed, one for the traces associated with passing tests and one for traces associated with failing tests. The dynamic call trees may be compared to determine subtree patterns that occur in one tree, but not the other. The subtree patterns may then be ranked based on the number of times the subtrees occur in the traces in order to determine the root cause of the non-deterministic test.

Description:
BACKGROUND 
     Software developers often use tests to validate the functionality of their source code when changes have been made to the codebase and prior to deploying the code to production. If the tests pass, developers may assume that their code is working correctly and if the tests fail, developers may determine that the source code has bugs or other problems that need to be fixed. However, some tests may be non-deterministic, meaning that there are times when the tests pass and times when the tests fail without any changes to the source code. 
     Non-deterministic tests may have several causes. For instance, some tests are written based on wrong assumptions about their behaviors such as assuming fixed execution time for a certain task. Other tests have unexpected resource sharing with other programs, such as network, file system and memory. Additionally, some tests contain concurrency bugs themselves. Non-determinism in software tests is a major impediment to effectively improving and evolving a codebase, especially as the codebase grows very large. These non-deterministic, flaky, tests can cause multiple problems including providing false success results when new code has a subtle non-deterministic bug or causing flaky libraries&#39; client applications to be unpredictable. Due to flakiness, it is difficult to reproduce the incorrect behavior and pinpoint the behavior&#39;s cause within source code. Therefore, as recognized by the inventors, there should be a tool to help developers effectively and efficiently determine the root cause of non-deterministic tests. 
     SUMMARY 
     This specification describes technologies related to software testing in general, and specifically to methods and systems for uncovering the root cause of non-deterministic software behaviors. 
     In general, one aspect of the subject matter described in this specification can be embodied in a system and method for determining the root cause of non-deterministic tests. An example system may include one or more processing devices and one or more storage devices storing instructions that, when executed by the one or more processing devices, cause the one or more processing devices to execute an example method. An example method may include: receiving a test that has been identified as non-deterministic; running the test, enabling call trace collection to record call traces from the test until at least one execution has succeeded and one execution has failed; for each trace in the call trace collection, determining whether the trace was part of a passing test or a failing test and labeling the trace according to the determination; constructing a dynamic call tree for the passing traces; constructing a dynamic call tree for the failing traces; comparing the dynamic call tree for the passing traces with the dynamic call tree for the failing traces to determine the subtree patterns that only occur in one tree or the other; and ranking the subtree patterns based on the number of hits in the traces to determine the root cause of the non-deterministic test. 
     These and other embodiments can optionally include one or more of the following features: generated classes may be renamed consistently across multiple test runs so that generated classes may be compared; class initialization may be factored out from the test runs to reduce the noise from non-deterministic class loading; non-deterministic class loading may be executed separately from the rest of the non-deterministic test in order to determine whether the non-deterministic test is flaky because of class initialization order; and subtree patterns may be mapped to the collected call trace and the overlapping subtree may be merged. 
     The details of one or more embodiments of the invention are set forth in the accompanying drawings which are given by way of illustration only, and the description below. Other features, aspects, and advantages of the invention will become apparent from the description, the drawings, and the claims. Like reference numbers and designations in the various drawings indicate like elements. 
    
    
     
       BRIEF DESCRIPTION OF THE DRAWINGS 
         FIG. 1  is a flow diagram of an exemplary method for determining the root cause of non-deterministic tests. 
         FIG. 2  is an example of a non-deterministic test. 
         FIG. 3 a    is an example call tree for a successful execution of a non-deterministic test. 
         FIG. 3 b    is an example call tree for a failing execution of a non-deterministic test. 
         FIG. 4 a    is an example subtree of the call tree of  FIG. 3 a    representing a successful execution of a non-deterministic test. 
         FIG. 4 b    is an example subtree of the call tree of  FIG. 3 a    representing a successful execution of a non-deterministic test. 
         FIG. 5 a    is an example subtree of the call tree of  FIG. 3 b    representing a failing execution of a non-deterministic test. 
         FIG. 5 b    is an example subtree of the call tree of  FIG. 3 b    representing a failing execution of a non-deterministic test. 
         FIG. 5 c    is an example subtree of the call tree of  FIG. 3 b    representing a failing execution of a non-deterministic test. 
         FIG. 6 a    is an example of subtrees of the call tree of  FIG. 3 a    representing a successful execution of a non-deterministic test that are not in the call tree of  FIG. 3 b    representing a failing execution of the non-deterministic test. 
         FIG. 6 b    is an example of subtrees of the call tree of  FIG. 3 b    representing a failing execution of a non-deterministic test that are not in the call tree of  FIG. 3 a    representing a successful execution of the non-deterministic test. 
         FIG. 7  is a block diagram illustrating an exemplary computing device. 
     
    
    
     DETAILED DESCRIPTION 
     According to an exemplary embodiment, as shown in  FIG. 1 , an exemplary system may receive non-deterministic or flaky tests, and run the tests with call trace collection enabled. A test is identified as non-deterministic if some of its runs execute successfully while the others end up with failure without any change to the code. An exemplary test, as shown in  FIG. 2  and written in pseudo code, verifies a condition to assign a task to a scheduler, schedule the task, and then check whether the task has been executed. 
     Call trace collection may be performed several different ways. In an exemplary embodiment, while a test is running, all function/method call histories may be sequentially recorded as (call depth, methodId). For instance, when at a call depth (or stack depth) of 10, method A calls method B first, method C, and then method D, pairs (10, A), (11, B), (11, C), and (11, D) may be recorded accordingly. All the call records together may construct a call trace and may be saved to a log file per thread. This call trace collection may be implemented through interpreter/compiler instrumentation or byte code rewriting. Alternatively, call relations may be sequentially recorded as method pairs (callerId, calleeId). For example, when method A calls B first, C next, and then D, pairs (A, B), (A, C), (A, D) are recorded accordingly. All the method pairs together construct a call trace and are saved to a log file. 
     For each execution of the test, the corresponding trace log file is labeled with either a “PASS” or “FAIL” to reflect the execution status. The test may be run at least enough times to receive one pass execution and one fail execution. More traces may produce better results. For each trace log file, an exemplary system constructs a dynamic call tree. 
     A dynamic call tree is an ordered tree where each node is a method and child nodes are actual call sequence from the method in the order of activation. For example, an edge going from node A to node B indicates that the procedure represented by node A calls the procedure represented by node B. 
       FIGS. 3 a  and 3 b    represent examples of call trees for the execution of a non-deterministic test.  FIG. 3 a    illustrates a successful run of the test while  FIG. 3 b    illustrates a failing run of the same test. These two trees have slightly different call traces, which indicate root cause of the non-determinism. 
     An exemplary system splits each tree into a set of smaller trees by truncating the whole tree into subtrees. In an exemplary embodiment, subtrees may be truncated by capturing subtrees to a depth of d. If there is a loop in the subtree at depth d, the subtree may be split at the back edge of the loop. The back edge may be obtained from the source code using static analysis. This method for truncating subtrees is only an example and any algorithm to capture subpatterns may work within an exemplary system. 
     Each subtree partially represents some call relations demonstrated in the overall trace. By comparing the subtree sets derived from PASS traces and those from FAIL traces, an exemplary system determines the subtree patterns unique to each set ( 113 ).  FIGS. 4 a  and 4 b    illustrate subtrees of the successful execution shown in  FIG. 3 a   .  FIGS. 5 a -5 c    illustrate subtrees of the failing execution shown in  FIG. 3 b   . By comparing the two sets, we find differences in the methods called by method B. 
     The exemplary system may then report patterns that only happen in PASS traces and subpatterns that only happen in FAIL traces. In some embodiments, the exemplary system may rank the diverging subtree patterns between the two sets based on the frequency of each pattern. The higher a pattern is ranked, the more likely it is to be a root cause of the non-deterministic test. 
     In some scenarios, source code contains some dynamically generated functions or classes at runtime. Each of such dynamically generated entity may contain the same code across different runs, but gets assigned different identity names. In order to get the actual difference between different traces ignoring such innocuous distinction, we take advantage of the naming convention for generated entities and map entities accordingly. For instance, an exemplary system maps class name CLASS_RUNA and CLASS_RUNB together based on their same prefix and aligns two traces on top of that. 
     In other scenarios, an exemplary system separates call traces relevant to class initialization from others in order to isolate effects caused by non-deterministic class loading. Then the call traces relevant to class initialization are analyzed separately to decide whether a different class loading order is the root cause for non-determinism. Using the class-initialization for the root causing analysis is optional. This is because many root causes are not related to the class-initialization, and this can be turned on only when the user may think it is relevant. 
     An exemplary system may also map subtree patterns to the collected call traces and merge the overlapping subtrees. If trees differ at a high level, the differences in the lower levels may be merged (or hidden) so an end user is not confused. Since an exemplary system may base its comparison on subtree patterns, several subtree patterns may actually overlap on the real tree. For example, the tree represented in  FIG. 3 a    may be written as (A (B (E F) C D) in parentheses notation. The tree in  FIG. 3 b    may be written as (A B (E) C (F) D). For every opening parenthesis, the tree descends one level and for every closing parenthesis, the tree ascends one level. If the tree in  FIG. 3 b    was (A B (E) X (F) D) instead of (A B (E) C (F) D), with method X replacing method C, then the patterns at the very top of the trees of  FIGS. 3 a  and 3 b    already differ. At the top level  FIG. 3 a    is (A (B C D) and revised  FIG. 3 b    is (A (B X D). Although there are also differences at the lower levels of the two trees, the lower-level differences are continuations of the differences at the top level. Therefore, an exemplary system combines these differences into a single pattern to display to the end user. Without combining the differences, the end user may assume that there are multiple differences when, in fact, there is one difference. 
       FIG. 7  is a high-level block diagram to show an application on a computing device ( 700 ). The application determines the root cause of flaky tests. In a basic configuration ( 701 ), the computing device ( 700 ) typically includes one or more processors ( 710 ), system memory ( 720 ), and a memory bus ( 730 ). The memory bus is used to do communication between processors and system memory. 
     Depending on different configurations, the processor ( 710 ) can be a microprocessor (μP), a microcontroller (μC), a digital signal processor (DSP), or any combination thereof. The processor ( 710 ) can include one or more levels of caching, such as a L1 cache ( 711 ) and a L2 cache ( 712 ), a processor core ( 713 ), and registers ( 714 ). The processor core ( 713 ) can include an arithmetic logic unit (ALU), a floating point unit (FPU), a digital signal processing core (DSP Core), or any combination thereof. A memory controller ( 716 ) can either be an independent part or an internal part of the processor ( 710 ). 
     Depending on the desired configuration, the system memory ( 720 ) can be of any type including but not limited to volatile memory (such as RAM), non-volatile memory (such as ROM, flash memory, etc.) or any combination thereof. System memory ( 720 ) typically includes an operating system ( 721 ), one or more applications ( 722 ), and program data ( 724 ). The application ( 722 ) may include a system and method for determining the root cause of non-deterministic tests. Program Data ( 724 ) includes storing instructions that, when executed by the one or more processing devices, implement a system and method for determining the root cause of non-deterministic tests. ( 723 ). In some embodiments, the application ( 722 ) can be arranged to operate with program data ( 724 ) on an operating system ( 721 ). 
     The computing device ( 700 ) can have additional features or functionality, and additional interfaces to facilitate communications between the basic configuration ( 701 ) and any required devices and interfaces. 
     System memory ( 720 ) is an example of computer storage media. Computer storage media includes, but is not limited to, RAM, ROM, EEPROM, flash memory or other memory technology, CD-ROM, digital versatile disks (DVD) or other optical storage, magnetic cassettes, magnetic tape, magnetic disk storage or other magnetic storage devices, or any other medium which can be used to store the desired information and which can be accessed by computing device  700 . Any such computer storage media can be part of the device ( 700 ). 
     The computing device ( 700 ) can be implemented as a portion of a small-form factor portable (or mobile) electronic device such as a cell phone, a smart phone, a personal data assistant (PDA), a personal media player device, a tablet computer (tablet), a wireless web-watch device, a personal headset device, an application-specific device, or a hybrid device that includes any of the above functions. The computing device ( 700 ) can also be implemented as a personal computer including both laptop computer and non-laptop computer configurations. 
     The foregoing detailed description has set forth various embodiments of the devices and/or processes via the use of block diagrams, flowcharts, and/or examples. Insofar as such block diagrams, flowcharts, and/or examples contain one or more functions and/or operations, it will be understood by those within the art that each function and/or operation within such block diagrams, flowcharts, or examples can be implemented, individually and/or collectively, by a wide range of hardware, software, firmware, or virtually any combination thereof. In one embodiment, several portions of the subject matter described herein may be implemented via Application Specific Integrated Circuits (ASICs), Field Programmable Gate Arrays (FPGAs), digital signal processors (DSPs), or other integrated formats. However, those skilled in the art will recognize that some aspects of the embodiments disclosed herein, in whole or in part, can be equivalently implemented in integrated circuits, as one or more computer programs running on one or more computers, as one or more programs running on one or more processors, as firmware, or as virtually any combination thereof, and that designing the circuitry and/or writing the code for the software and/or firmware would be well within the skill of one skilled in the art in light of this disclosure. In addition, those skilled in the art will appreciate that the mechanisms of the subject matter described herein are capable of being distributed as a program product in a variety of forms, and that an illustrative embodiment of the subject matter described herein applies regardless of the particular type of non-transitory signal bearing medium used to actually carry out the distribution. Examples of a non-transitory signal bearing medium include, but are not limited to, the following: a recordable type medium such as a floppy disk, a hard disk drive, a Compact Disc (CD), a Digital Video Disk (DVD), a digital tape, a computer memory, etc.; and a transmission type medium such as a digital and/or an analog communication medium. (e.g., a fiber optic cable, a waveguide, a wired communications link, a wireless communication link, etc.) 
     With respect to the use of any plural and/or singular terms herein, those having skill in the art can translate from the plural to the singular and/or from the singular to the plural as is appropriate to the context and/or application. The various singular/plural permutations may be expressly set forth herein for sake of clarity. 
     Thus, particular embodiments of the subject matter have been described. Other embodiments are within the scope of the following claims. In some cases, the actions recited in the claims can be performed in a different order and still achieve desirable results. In addition, the processes depicted in the accompanying figures do not necessarily require the particular order shown, or sequential order, to achieve desirable results. In certain implementations, multitasking and parallel processing may be advantageous.