Automatically Generating Precise Oracles from Structured Natural Language Specifications
Manish Motwani Yuriy Brun
from Structured Natural Language Specifications - - PowerPoint PPT Presentation
Automatically Generating Precise Oracles from Structured Natural Language Specifications http://swami.cs.umass.edu Manish Motwani Yuriy Brun The Test Oracle Problem Software Under Input Actual Output Test The Test Oracle Problem Software
Manish Motwani Yuriy Brun
Input Software Under Test Actual Output
Input Software Under Test Actual Output Test Oracle (Expected Output) Correct Incorrect
Input Software Under Test Actual Output Test Oracle (Expected Output) Correct Incorrect
Easy to generate
Input Software Under Test Actual Output Test Oracle (Expected Output) Correct Incorrect
Easy to generate Hard to generate
Structured Informal Specification
Swami
/*TEST TEMPLATE WITH ORACLE*/ function test_array_len( len ){ if ( ToUint32(len)!=len) { try{ var output = new Array ( len ); return; }catch(e){ assert.strictEqual(true, (e instanceof RangeError)); return; } } } /*TEST INPUTS*/ test_array_len(1.1825863363010669e+308); test_array_len(null); test_array_len(-747); test_array_len(368); …
Structured Informal Specification Executable Test
http://swami.cs.umass.edu
Test oracle Test inputs
Does not get deprecated
Does not get deprecated Less ambiguous
Does not get deprecated Less ambiguous Multiple real-world projects adhere to the spec
50,086
Number of Tests (total 83,000)
Innocuous tests (60.4%)
50,086
Number of Tests (total 83,000)
Innocuous tests Good tests (60.4%) 32,379 (39.0%)
50,086 32,379 535
Number of Tests (total 83,000)
(0.6%) Innocuous tests Good tests (60.4%) 32,379 (39.0%) Bad tests
50,086 32,379 535
Number of Tests (total 83,000)
(0.6%) Innocuous tests Good tests (60.4%) 32,379 (39.0%) Bad tests
Missing Features / Bugs
JavaScript specification
Missing Features / Bugs
JavaScript specification
line coverage branch coverage
Code Coverage Ratio
Developer Developer+Swami 15.2% 19.3%
line coverage
Code Coverage Ratio
EvoSuite EvoSuite+Swami 19.5% bad tests
Number of False Alarms
EvoSuite Swami 73.9%
0.00% 20.00% 40.00% 60.00% 80.00% 100.00%
performance using rule-based approach
precision recall 0.00% 20.00% 40.00% 60.00% 80.00% 100.00%
performance using IR-based approach
precision recall
Encode testable behavior
Encode testable behavior Abstract Operations
Implicit Operations Encode testable behavior Abstract Operations
Implicit Operations Oracles embedded in Conditionals Encode testable behavior Abstract Operations
Implicit Operations Encode testable behavior Abstract Operations Oracles embedded in Conditionals Assignments using local variables
Implicit Operations Ambiguous and Deprecated Encode testable behavior Abstract Operations Oracles embedded in Conditionals Assignments using local variables
and Randoop, hence may not generalize
Implicit Operations Ambiguous and Deprecated Encode testable behavior Abstract Operations Oracles embedded in Conditionals Assignments using local variables
and Randoop, hence may not generalize State-of-the-art tools are not capable of deriving test oracles from informal specifications that exists independent of the source code.
Implicit Operations Ambiguous and Deprecated Encode testable behavior Abstract Operations Oracles embedded in Conditionals Assignments using local variables
Vague oracles for common inputs Concrete oracles for uncommon inputs
Vague oracles for common inputs Concrete oracles for uncommon inputs
Informal specifications typically contain oracles for Exceptions and Boundary conditions.
Source: Goffi, Alberto, et al. “Automatic generation of oracles for exceptional behaviors.” ISSTA, 2016.
Source: Goffi, Alberto, et al. “Automatic generation of oracles for exceptional behaviors.” ISSTA, 2016.
Exceptions are under-tested by the developers
Automatically generate executable tests (inputs with oracles) for Exceptions and Boundary conditions from structured informal specifications
Implicit Operations Ambiguous and deprecated encode testable behavior Abstract Operations Oracles embedded in Conditionals Assignments using local variables
Structured Informal specification Executable Test
Test inputs Test oracles
Automatically generate executable tests (inputs with oracles) for Exceptions and Boundary conditions from structured informal specifications
Structured Informal specification Executable Test
Test inputs Test oracles Implicit Operations Ambiguous and deprecated encode testable behavior Abstract Operations Oracles embedded in Conditionals Assignments using local variables
Rule-based approach
Specification Document Relevant Specifications
Rules are regular expressions composed of POS tags, keywords, and wild card characters
Rule-based approach
Specification Document Relevant Specifications
Heading RE: [CD new* NN LRB NN.* RRB] Body RE: [If .* return .*] [if .* throw .* exception]
Rule-based approach Information Retrieval-based approach
Source code
when the format of specification document is unknown
Specification Document Relevant Specifications
OKAPI model
Relevant Specifications
Header RE: CD new* NN LRB NN.* RRB
Relevant Specifications
Header RE: CD new* NN LRB NN.* RRB Body RE: If .* return .* Body RE: If .* throw .* exception
function test_string_prototype_startswith(thisObj,searchString,position) {} function test_< method name >(thisObj,<[ method args ]>) {}
Initialized Test Template
function test_string_prototype_startswith(thisObj,searchString,position) {} function test_< method name >(thisObj,<[ method args ]>) {}
new String(thisObj).startsWith(searchString, position); Method invocation code Initialized Test Template
Variable Value O RequireObjectCoercible(this value) S ToString(O) isRegExp IsRegExp(searchString) searchStr ToString(searchString) pos ToInteger(position) len length of S start min(max(pos,0),len) searchLength length of searchStr
if (<condition>) { var output = <method invocation>; <test constructor>(output, <expected output>); return; }
Boundary Condition Exception
if (<condition>) { try { var output = <method invocation>; return; }catch(e){ <test constructor>(true, (e instance of <expected error>)); return; } }
Exception
Boundary condition oracle Exception oracle
if (<condition>) { var output = <method invocation>; <test constructor>(output, <expected output>); return; } if (<condition>) { try { var output = <method invocation>; return; }catch(e){ <test constructor>(true, (e instance of <expected error>)); return; } }
Boundary Condition Exception
if (isRegExp is true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } } if (<condition>) { try { var output = <method invocation>; return; }catch(e){ <test constructor>(true, (e instance of <expected error>)); return; } }
Exception
Exception oracle
if (isRegExp is true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } } if (<condition>) { try { var output = <method invocation>; return; }catch(e){ <test constructor>(true, (e instance of <expected error>)); return; } }
Exception
Exception oracle
Exception oracle
if (isRegExp is true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } } if (<condition>) { try { var output = <method invocation>; return; }catch(e){ <test constructor>(true, (e instance of <expected error>)); return; } }
Exception
From step2
Exception oracle
if (isRegExp is true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } } if (<condition>) { try { var output = <method invocation>; return; }catch(e){ <test constructor>(true, (e instance of <expected error>)); return; } }
Exception
Input by developer From step2
Boundary condition oracle
if (searchLength+start is greater than len){ var output = new String(thisObj).startsWith(searchString, position); assert.strictEqual(output, false); return; } if (<condition>) { var output = <method invocation>; <test constructor>(output, <expected output>); return; }
Boundary Condition
if (searchLength+start is greater than len){ var output = new String(thisObj).startsWith(searchString, position); assert.strictEqual(output, false); return; } if (isRegExp is true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } } Variable Value O RequireObjectCoercible(this value) S ToString(O) isRegExp IsRegExp(searchString) searchStr ToString(searchString) pos ToInteger(position) len length of S start min(max(pos,0),len) searchLength length of searchStr
Method Arguments: thisObj searchString position
if (ToString(searchString).length + Math.min(Math.max(ToInteger(position), 0), ToString(RequireObjectCoercible(thisObj)).length) > ToString(RequireObjectCoercible(thisObj)).length){ var output = new String(thisObj).startsWith(searchString, position); assert.strictEqual(output, false); return; } if (IsRegExp(searchString) === true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } }
Variable Value O RequireObjectCoercible(this value) S ToString(O) isRegExp IsRegExp(searchString) searchStr ToString(searchString) pos ToInteger(position) len length of S start min(max(pos,0),len) searchLength length of searchStr
Method Arguments: thisObj searchString position
function test_string_prototype_startswith(thisObj,searchString,position) { } if (IsRegExp(searchString) === true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } }
function test_string_prototype_startswith(thisObj,searchString,position) { } if (IsRegExp(searchString) === true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } }
function IsRegExp(argument){ return (argument instanceof RegExp); } …
function test_string_prototype_startswith(thisObj,searchString,position) { } if (ToString(searchString).length + Math.min(Math.max(ToInteger(position), 0), ToString(RequireObjectCoercible(thisObj)).length) > ToString(RequireObjectCoercible(thisObj)).length){ var output = new String(thisObj).startsWith(searchString, position); assert.strictEqual(output, false); return; } if (IsRegExp(searchString) === true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } } function IsRegExp(argument){ return (argument instanceof RegExp); } …
Abstract Operations Test Template encoding Oracles
valid string
test_string_prototype_startswith("Y3I9", "E0RS6GU078", 894); test_string_prototype_startswith("T82LL6", 572, false); test_string_prototype_startswith("XU6W0", "J3A", Infinity); test_string_prototype_startswith("W5E74X0R", null, NaN); ...
function test_string_prototype_startswith(thisObj,searchString,position) { } if (ToString(searchString).length + Math.min(Math.max(ToInteger(position), 0), ToString(RequireObjectCoercible(thisObj)).length) > ToString(RequireObjectCoercible(thisObj)).length){ var output = new String(thisObj).startsWith(searchString, position); assert.strictEqual(output, false); return; } if (IsRegExp(searchString) === true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } } function IsRegExp(argument){ return (argument instanceof RegExp); } …
Abstract Operations Test Template encoding Oracles
function test_string_prototype_startswith(thisObj,searchString,position) { } if (ToString(searchString).length + Math.min(Math.max(ToInteger(position), 0), ToString(RequireObjectCoercible(thisObj)).length) > ToString(RequireObjectCoercible(thisObj)).length){ var output = new String(thisObj).startsWith(searchString, position); assert.strictEqual(output, false); return; } if (IsRegExp(searchString) === true){ try{ var output = new String(thisObj).startsWith(searchString, position); return; }catch(e){ assert.StrictEqual(true,(e instanceof TypeError)); return; } } function IsRegExp(argument){ return (argument instanceof RegExp); }
Abstract Operations
test_string_prototype_startswith("Y3I9", "E0RS6GU078", 894); test_string_prototype_startswith("T82LL6", 572, false); test_string_prototype_startswith("XU6W0", "J3A", Infinity); test_string_prototype_startswith("W5E74X0R", null, NaN); ...
Test Template encoding Oracles Test Inputs
Executable Test with Oracles
http://swami.cs.umass.edu
http://swami.cs.umass.edu
http://swami.cs.umass.edu
http://people.cs.umass.edu/~mmotwani