[gjs] coverage: Handle numerous other expression types in coverage.js
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs] coverage: Handle numerous other expression types in coverage.js
- Date: Sun, 18 Jan 2015 21:39:58 +0000 (UTC)
commit 5072cd737c605436bb48bc24a606144105e6912e
Author: Sam Spilsbury <smspillaz gmail com>
Date: Wed Jan 7 14:37:08 2015 +0800
coverage: Handle numerous other expression types in coverage.js
Certain expressions, like for (... in), for (... of), yield,
array comprehensions, logical expressions (a || b), etc were not
being traversed. These could contain function declarations. This
fixes numerous cases where an assertion might be raised by the
coverage machinery where the debugger enters a function it does
not know about.
Fixes #742535
installed-tests/js/testCoverage.js | 302 +++++++++++++++++++++++++++++++++++-
modules/coverage.js | 68 +++++++--
test/gjs-test-coverage.cpp | 42 +++++
3 files changed, 397 insertions(+), 15 deletions(-)
---
diff --git a/installed-tests/js/testCoverage.js b/installed-tests/js/testCoverage.js
index 30b0da1..f22c3e9 100644
--- a/installed-tests/js/testCoverage.js
+++ b/installed-tests/js/testCoverage.js
@@ -34,7 +34,6 @@ function testExpressionLinesFoundForAssignmentExpressionSides() {
JSUnit.assertEquals);
}
-
function testExpressionLinesFoundForLinesInsideFunctions() {
let foundLinesInsideNamedFunction =
parseScriptForExpressionLines("function f(a, b) {\n" +
@@ -183,7 +182,7 @@ function testExpressionLinesFoundForIfExits() {
JSUnit.assertEquals);
}
-function testExpressionLinesFoundForFirstLineOfMultilineIfTests() {
+function testExpressionLinesFoundForAllLinesOfMultilineIfTests() {
let foundLinesInsideMultilineIfTest =
parseScriptForExpressionLines("if (1 > 0 &&\n" +
" 2 > 0 &&\n" +
@@ -191,7 +190,7 @@ function testExpressionLinesFoundForFirstLineOfMultilineIfTests() {
" let a = 3;\n" +
"}\n");
assertArrayEquals(foundLinesInsideMultilineIfTest,
- [1, 4],
+ [1, 2, 3, 4],
JSUnit.assertEquals);
}
@@ -331,6 +330,303 @@ function testFunctionsFoundOnSameLineButDifferentiatedOnArgs() {
functionDeclarationsEqual);
}
+function testFunctionsInsideArrayExpression() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let a = [function() {}];\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 },
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideArrowExpression() {
+ let foundFunctions =
+ parseScriptForFunctionNames("(a) => (function(){})();\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideSequence() {
+ let foundFunctions =
+ parseScriptForFunctionNames("(function(a){})()," +
+ "(function(a, b){})();\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 1 },
+ { name: null, line: 1, n_params: 2 },
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideUnaryExpression() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let a = (function () {}())++;\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 },
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideBinaryExpression() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let a = function (a) {}() +" +
+ " function (a, b) {}();\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 1 },
+ { name: null, line: 1, n_params: 2 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideAssignmentExpression() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let a = function () {}();\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideUpdateExpression() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let a;\n" +
+ "a += function () {}();\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 2, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideIfConditions() {
+ let foundFunctions =
+ parseScriptForFunctionNames("if (function (a) {}(a) >" +
+ " function (a, b) {}(a, b)) {};\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 1 },
+ { name: null, line: 1, n_params: 2 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideWhileConditions() {
+ let foundFunctions =
+ parseScriptForFunctionNames("while (function (a) {}(a) >" +
+ " function (a, b) {}(a, b)) {};\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 1 },
+ { name: null, line: 1, n_params: 2 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideForInitializer() {
+ let foundFunctions =
+ parseScriptForFunctionNames("for (function() {};;) {}\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+/* SpiderMonkey parses for (let i = <init>; <cond>; <update>) as though
+ * they were let i = <init> { for (; <cond> <update>) } so test the
+ * LetStatement initializer case too */
+function testFunctionsInsideForLetInitializer() {
+ let foundFunctions =
+ parseScriptForFunctionNames("for (let i = function() {};;) {}\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideForVarInitializer() {
+ let foundFunctions =
+ parseScriptForFunctionNames("for (var i = function() {};;) {}\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideForCondition() {
+ let foundFunctions =
+ parseScriptForFunctionNames("for (; function() {}();) {}\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideForIncrement() {
+ let foundFunctions =
+ parseScriptForFunctionNames("for (;;function() {}()) {}\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideForInObject() {
+ let foundFunctions =
+ parseScriptForFunctionNames("for (let x in function() {}()) {}\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideForInObject() {
+ let foundFunctions =
+ parseScriptForFunctionNames("for each (x in function() {}()) {}\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInsideForOfObject() {
+ let foundFunctions =
+ parseScriptForFunctionNames("for (x of (function(){}())) {}\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsUsedAsObjectFound() {
+ let foundFunctions =
+ parseScriptForFunctionNames("f = function() { }.bind();\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsUsedAsObjectDynamicProp() {
+ let foundFunctions =
+ parseScriptForFunctionNames("f = function() { }['bind']();\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsOnEitherSideOfLogicalExpression() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let f = function(a) { } ||" +
+ " function(a, b) { };\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 1, n_params: 1 },
+ { name: null, line: 1, n_params: 2 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsOnEitherSideOfConditionalExpression() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let a\n" +
+ "let f = a ? function(a) { }() :" +
+ " function(a, b) { }();\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 2, n_params: 1 },
+ { name: null, line: 2, n_params: 2 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsYielded() {
+ let foundFunctions =
+ parseScriptForFunctionNames("function a () { yield function (){} };\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: "a", line: 1, n_params: 0 },
+ { name: null, line: 1, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInArrayComprehensionBody() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let a = new Array(1);\n" +
+ "let b = [function () {} for (i of a)];\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 2, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInArrayComprehensionBlock() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let a = new Array(1);\n" +
+ "let b = [i for (i of function () {})];\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 2, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
+function testFunctionsInArrayComprehensionFilter() {
+ let foundFunctions =
+ parseScriptForFunctionNames("let a = new Array(1);\n" +
+ "let b = [i for (i of a)" +
+ "if (function () {} ())];\n");
+
+ assertArrayEquals(foundFunctions,
+ [
+ { name: null, line: 2, n_params: 0 }
+ ],
+ functionDeclarationsEqual);
+}
+
function parseScriptForBranches(script) {
const ast = Reflect.parse(script);
return Coverage.branchesForAST(ast);
diff --git a/modules/coverage.js b/modules/coverage.js
index 1c29992..4609069 100644
--- a/modules/coverage.js
+++ b/modules/coverage.js
@@ -28,15 +28,15 @@ function getSubNodesForNode(node) {
/* These statements have a single body */
case 'LabelledStatement':
case 'WithStatement':
- case 'LetStatement':
- case 'ForInStatement':
- case 'ForOfStatement':
case 'FunctionDeclaration':
case 'FunctionExpression':
- case 'ArrowExpression':
case 'CatchClause':
subNodes.push(node.body);
break;
+ case 'LetStatement':
+ Array.prototype.push.apply(subNodes, node.head);
+ subNodes.push(node.body);
+ break;
case 'WhileStatement':
case 'DoWhileStatement':
subNodes.push(node.body);
@@ -52,11 +52,21 @@ function getSubNodesForNode(node) {
subNodes.push(node.body);
break;
+ case 'ForInStatement':
+ if (node.each)
+ subNodes.push(node.left);
+
+ subNodes.push(node.right, node.body);
+ break;
+ case 'ForOfStatement':
+ subNodes.push(node.left, node.right, node.body);
+ break;
case 'BlockStatement':
Array.prototype.push.apply(subNodes, node.body);
break;
case 'ThrowStatement':
case 'ReturnStatement':
+ case 'YieldExpression':
if (node.argument !== null)
subNodes.push(node.argument);
break;
@@ -64,13 +74,45 @@ function getSubNodesForNode(node) {
subNodes.push(node.expression);
break;
case 'AssignmentExpression':
+ case 'BinaryExpression':
+ case 'LogicalExpression':
subNodes.push(node.left, node.right);
break;
+ case 'ConditionalExpression':
+ subNodes.push(node.test, node.consequent, node.alternate);
+ break;
case 'ObjectExpression':
node.properties.forEach(function(prop) {
subNodes.push(prop.value);
});
break;
+ case 'ArrayExpression':
+ node.elements.forEach(function(elem) {
+ if (elem !== null)
+ subNodes.push(elem);
+ });
+ break;
+ case 'ArrowExpression':
+ Array.prototype.push.apply(subNodes, node.defaults);
+ subNodes.push(node.body);
+ break;
+ case 'SequenceExpression':
+ Array.prototype.push.apply(subNodes, node.expressions);
+ break;
+ case 'UnaryExpression':
+ case 'UpdateExpression':
+ subNodes.push(node.argument);
+ break;
+ case 'ComprehensionExpression':
+ case 'GeneratorExpression':
+ subNodes.push(node.body);
+ Array.prototype.push.apply(subNodes, node.blocks);
+ if (node.filter !== null)
+ subNodes.push(node.filter);
+ break;
+ case 'ComprehensionBlock':
+ subNodes.push(node.right);
+ break;
/* It is very possible that there might be something
* interesting in the function arguments, so we need to
* walk them too */
@@ -99,16 +141,18 @@ function getSubNodesForNode(node) {
subNodes.push(expression);
});
}
-
break;
- /* Variable declarations might be initialized to
- * some expression, so traverse the tree and see if
- * we can get into the expression */
case 'VariableDeclaration':
- node.declarations.forEach(function (declarator) {
- if (declarator.init !== null)
- subNodes.push(declarator.init);
- });
+ Array.prototype.push.apply(subNodes, node.declarations);
+ break;
+ case 'VariableDeclarator':
+ if (node.init !== null)
+ subNodes.push(node.init);
+ break;
+ case 'MemberExpression':
+ subNodes.push(node.object);
+ if (node.computed)
+ subNodes.push(node.property);
break;
}
diff --git a/test/gjs-test-coverage.cpp b/test/gjs-test-coverage.cpp
index 28929a7..de4a4e8 100644
--- a/test/gjs-test-coverage.cpp
+++ b/test/gjs-test-coverage.cpp
@@ -1157,6 +1157,44 @@ test_single_line_hit_written_to_coverage_data(gpointer fixture_data,
}
static void
+test_hits_on_multiline_if_cond(gpointer fixture_data,
+ gconstpointer user_data)
+{
+ GjsCoverageToSingleOutputFileFixture *fixture = (GjsCoverageToSingleOutputFileFixture *) fixture_data;
+
+ const char *script_with_multine_if_cond =
+ "let a = 1;\n"
+ "let b = 1;\n"
+ "if (a &&\n"
+ " b) {\n"
+ "}\n";
+
+ write_to_file_at_beginning(fixture->base_fixture.temporary_js_script_open_handle,
+ script_with_multine_if_cond);
+
+ char *coverage_data_contents =
+ eval_script_and_get_coverage_data(fixture->base_fixture.context,
+ fixture->base_fixture.coverage,
+ fixture->base_fixture.temporary_js_script_filename,
+ fixture->output_file_directory,
+ NULL);
+
+ /* Hits on all lines, including both lines with a condition (3 and 4) */
+ LineCountIsMoreThanData data[] = {
+ { 1, 0 },
+ { 2, 0 },
+ { 3, 0 },
+ { 4, 0 }
+ };
+
+ g_assert(coverage_data_matches_value_for_key(coverage_data_contents,
+ "DA:",
+ line_hit_count_is_more_than,
+ data));
+ g_free(coverage_data_contents);
+}
+
+static void
test_full_line_tally_written_to_coverage_data(gpointer fixture_data,
gconstpointer user_data)
{
@@ -1512,6 +1550,10 @@ void gjs_test_add_tests_for_coverage()
&coverage_to_single_output_fixture,
test_single_line_hit_written_to_coverage_data,
NULL);
+ add_test_for_fixture("/gjs/coverage/hits_on_multiline_if_cond",
+ &coverage_to_single_output_fixture,
+ test_hits_on_multiline_if_cond,
+ NULL);
add_test_for_fixture("/gjs/coverage/full_line_tally_written_to_coverage_data",
&coverage_to_single_output_fixture,
test_full_line_tally_written_to_coverage_data,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]