Commit a163f802 authored by Paul Lewis's avatar Paul Lewis Committed by Commit Bot

[DevTools] Adds support for Karma tests

Change-Id: I1ea19eee040360d3a73ffaa72ccf15bceff80317
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1806858
Auto-Submit: Paul Lewis <aerotwist@google.com>
Reviewed-by: default avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Paul Lewis <aerotwist@google.com>
Cr-Commit-Position: refs/heads/master@{#696789}
parent c1fe633f
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module.exports = function(config) {
const options = {
basePath: "",
files: [{
pattern: 'front_end/**/*.js',
included: false,
served: true
},{
pattern: 'tests/**/*.ts',
type: 'module'
}],
reporters: ["dots"],
preprocessors: {
'./tests/**/*.ts': ['karma-typescript']
},
browsers: ["ChromeHeadless"],
frameworks: ["mocha", "chai", "karma-typescript"],
karmaTypescriptConfig: {
compilerOptions: {
target: "esnext",
module: "esnext",
typeRoots: ["./third_party/devtools-node-modules/third_party/node_modules/@types"]
},
coverageOptions: {
instrumentation: false
},
bundlerOptions: {
resolve: {
directories: ["./third_party/devtools-node-modules/third_party/node_modules"]
}
}
},
proxies: {
'/front_end': '/base/front_end',
},
plugins: [
"karma-chrome-launcher",
"karma-mocha",
"karma-chai",
"karma-typescript"
],
singleRun: true
};
config.set(options);
};
...@@ -61,11 +61,11 @@ One-time setup: ...@@ -61,11 +61,11 @@ One-time setup:
npm run setup-dtrun npm run setup-dtrun
``` ```
Now, you can use any of the following commands by simply doing: `dtrun test`. Now, you can use any of the following commands by simply doing: `dtrun test`.
In addition, you no longer need to pass double dashes (e.g. `--`) before you pass in the flags. So you can do: `dtrun test -d inspector/test.html`. In addition, you no longer need to pass double dashes (e.g. `--`) before you pass in the flags. So you can do: `dtrun test -d inspector/test.html`.
#### `npm run format` #### `npm run format`
Formats your code using clang-format Formats your code using clang-format
### `npm run format-py` ### `npm run format-py`
...@@ -85,8 +85,8 @@ npm test -- inspector/sources inspector/console ...@@ -85,8 +85,8 @@ npm test -- inspector/sources inspector/console
# debug a specific test. Any one of: # debug a specific test. Any one of:
npm run debug-test inspector/cookie-resource-match.html npm run debug-test inspector/cookie-resource-match.html
npm test -- --debug-devtools inspector/cookie-resource-match.html npm test -- --debug-devtools inspector/cookie-resource-match.html
npm test -- -d inspector/cookie-resource-match.html npm test -- -d inspector/cookie-resource-match.html
# pass in additional flags to the test harness # pass in additional flags to the test harness
npm test -- -f --child-processes=16 npm test -- -f --child-processes=16
...@@ -99,15 +99,15 @@ npm test -- --time-out-ms=6000000 <test_path> ...@@ -99,15 +99,15 @@ npm test -- --time-out-ms=6000000 <test_path>
#### `--fetch-content-shell` #### `--fetch-content-shell`
``` ```
# If you're using a full chromium checkout and have a compiled content shell, # If you're using a full chromium checkout and have a compiled content shell,
# this will fetch a pre-compiled content shell. This is useful if you # this will fetch a pre-compiled content shell. This is useful if you
# haven't compiled your content shell recently # haven't compiled your content shell recently
npm test -- --fetch-content-shell npm test -- --fetch-content-shell
``` ```
#### `--target=SUB_DIRECTORY_NAME` #### `--target=SUB_DIRECTORY_NAME`
``` ```
# If you're using a build sub-directory that's not out/Release, # If you're using a build sub-directory that's not out/Release,
# such as out/Default, then use --target=SUB_DIRECTORY_NAME # such as out/Default, then use --target=SUB_DIRECTORY_NAME
npm test -- --target=Default npm test -- --target=Default
``` ```
...@@ -127,3 +127,17 @@ npm test -- --target=Default ...@@ -127,3 +127,17 @@ npm test -- --target=Default
[@ChromeDevTools]: http://twitter.com/ChromeDevTools [@ChromeDevTools]: http://twitter.com/ChromeDevTools
[@DevToolsCommits]: http://twitter.com/DevToolsCommits [@DevToolsCommits]: http://twitter.com/DevToolsCommits
[all open DevTools tickets]: https://bugs.chromium.org/p/chromium/issues/list?can=2&q=component%3APlatform%3EDevTools&sort=&groupby=&colspec=ID+Stars+Owner+Summary+Modified+Opened [all open DevTools tickets]: https://bugs.chromium.org/p/chromium/issues/list?can=2&q=component%3APlatform%3EDevTools&sort=&groupby=&colspec=ID+Stars+Owner+Summary+Modified+Opened
### Tests
The tests are run through Karma.
```
python scripts/run_tests.py
```
You can also specify with which Chrome binary to run tests by setting the
`chrome-binary` variable.
```
python scripts/run_tests.py --chrome-binary=/path/to/chromium/build/chromium
```
...@@ -12,6 +12,8 @@ SCRIPTS_PATH = path.dirname(path.abspath(__file__)) ...@@ -12,6 +12,8 @@ SCRIPTS_PATH = path.dirname(path.abspath(__file__))
THIRD_PARTY_PATH = path.join(SCRIPTS_PATH, '..', '..', '..', '..') THIRD_PARTY_PATH = path.join(SCRIPTS_PATH, '..', '..', '..', '..')
NODE_PATH = path.join(THIRD_PARTY_PATH, 'node') NODE_PATH = path.join(THIRD_PARTY_PATH, 'node')
ESLINT_PATH = path.join(THIRD_PARTY_PATH, 'devtools-node-modules', 'third_party', 'node_modules', 'eslint', 'bin', 'eslint.js') ESLINT_PATH = path.join(THIRD_PARTY_PATH, 'devtools-node-modules', 'third_party', 'node_modules', 'eslint', 'bin', 'eslint.js')
KARMA_PATH = path.join(THIRD_PARTY_PATH, 'devtools-node-modules', 'third_party', 'node_modules', 'karma', 'bin', 'karma')
NODE_MODULES_PATH = path.join(THIRD_PARTY_PATH, 'devtools-node-modules', 'third_party', 'node_modules')
try: try:
old_sys_path = sys.path[:] old_sys_path = sys.path[:]
...@@ -27,3 +29,11 @@ def node_path(): ...@@ -27,3 +29,11 @@ def node_path():
def eslint_path(): def eslint_path():
return ESLINT_PATH return ESLINT_PATH
def karma_path():
return KARMA_PATH
def node_modules_path():
return NODE_MODULES_PATH
#!/usr/bin/env python
#
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import re
import subprocess
import sys
import local_node
is_cygwin = sys.platform == "cygwin"
chrome_binary = None
if len(sys.argv) >= 2:
chrome_binary = re.sub(r"^\-\-chrome-binary=(.*)", "\\1", sys.argv[1])
is_executable = os.path.exists(chrome_binary) and os.path.isfile(chrome_binary) and os.access(chrome_binary, os.X_OK)
if not is_executable:
print("Unable to find a Chrome binary at \"%s\"" % chrome_binary)
sys.exit(1)
def popen(arguments, cwd=None, env=None):
return subprocess.Popen(arguments, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)
def to_platform_path_exact(filepath):
if not is_cygwin:
return filepath
output, _ = popen(["cygpath", "-w", filepath]).communicate()
# pylint: disable=E1103
return output.strip().replace("\\", "\\\\")
scripts_path = os.path.dirname(os.path.abspath(__file__))
devtools_path = os.path.dirname(scripts_path)
print("Running tests with Karma...")
if (chrome_binary is not None):
print("Using custom Chrome Binary (%s)\n" % chrome_binary)
else:
print("Using system Chrome")
def run_tests():
karma_errors_found = False
karmaconfig_path = os.path.join(devtools_path, "karma.conf.js")
exec_command = [local_node.node_path(), local_node.karma_path(), "start", to_platform_path_exact(karmaconfig_path)]
env = {'NODE_PATH': local_node.node_modules_path()}
if (chrome_binary is not None):
env['CHROME_BIN'] = chrome_binary
karma_proc = popen(exec_command, cwd=devtools_path, env=env)
(karma_proc_out, _) = karma_proc.communicate()
if karma_proc.returncode != 0:
karma_errors_found = True
else:
print("Karma exited successfully")
print(karma_proc_out)
return karma_errors_found
errors_found = run_tests()
if errors_found:
print("ERRORS DETECTED")
sys.exit(1)
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const { assert } = chai;
// FIXME: Convert to pure functions as these utilities have side effects.
import '../../../front_end/platform/utilities.js';
declare global {
interface Array<T> {
remove(value: T, firstOnly: boolean): boolean;
mergeOrdered(array: T[], comparator: (a: T, b: T) => number): T[];
intersectOrdered(array: T[], comparator: (a: T, b: T) => number): T[];
upperBound(value: T, comparator?: (a: T, b: T) => number): number;
lowerBound(value: T, comparator?: (a: T, b: T) => number): number;
binaryIndexOf(value: T, comparator: (a: T, b: T) => number): number;
qselect(idx: number): number;
sortRange(comparator: (a: T, b: T) => number, leftBound: number, rightBound: number, sortWindowLeft: number, sortWindowRight: number): T[];
}
interface String {
trimURL(base: string): string;
toBase64(): string;
trimMiddle(maxLength: number): string;
repeat(length: number): string;
}
interface StringConstructor {
hashCode(value: string): number;
naturalOrderComparator(a: string, b: string): number;
}
}
describe('Utilities', () => {
it('removes values', () => {
const testArrays = [
[], [], [], [1], [1], [1], [1, 2, 3, 4, 5, 4, 3, 2, 1], [1, 3, 4, 5, 4, 3, 2, 1], [1, 3, 4, 5, 4, 3, 1],
[2, 2, 2, 2, 2], [2, 2, 2, 2], [], [2, 2, 2, 1, 2, 2, 3, 2], [2, 2, 1, 2, 2, 3, 2], [1, 3]
];
for (let i = 0; i < testArrays.length; i += 3) {
let actual = testArrays[i].slice(0);
let expected = testArrays[i + 1];
actual.remove(2, true);
assert.deepStrictEqual(expected, actual, 'Removing firstOnly (true) failed');
actual = testArrays[i].slice(0);
expected = testArrays[i + 2];
actual.remove(2, false);
assert.deepStrictEqual(expected, actual, 'Removing firstOnly (false) failed');
}
});
it('orders merge intersect', () => {
function comparator(a: number, b: number) {
return a - b;
}
function count(a: number[], x: number) {
return a.upperBound(x) - a.lowerBound(x);
}
function testAll(a: number[], b: number[]) {
testOperation(a, b, a.mergeOrdered(b, comparator), Math.max, 'U');
testOperation(a, b, a.intersectOrdered(b, comparator), Math.min, 'x');
}
function testOperation(a: number[], b: number[], actual: number[], checkOperation: (...values: number[]) => number, opName: string) {
const allValues = a.concat(b).concat(actual);
let expectedCount: number;
let actualCount: number;
for (let i = 0; i < allValues.length; ++i) {
let value = allValues[i];
expectedCount = checkOperation(count(a, value), count(b, value));
actualCount = count(actual, value);
assert.equal(expectedCount, actualCount,
'Incorrect result for value: ' + value + ' at [' + a + '] ' + opName + ' [' + b + '] -> [' + actual +
']');
}
// FIXME: This appears to be a no-op given the fact that sort works in-place.
assert.deepStrictEqual(actual.sort(), actual, 'Result array is ordered');
}
const testArrays = [
[], [], [1], [], [1, 2, 2, 2, 3], [], [4, 5, 5, 8, 8], [1, 1, 1, 2, 6], [1, 2, 2, 2, 2, 3, 3, 4],
[2, 2, 2, 3, 3, 3, 3], [1, 2, 3, 4, 5], [1, 2, 3]
];
for (let i = 0; i < testArrays.length; i += 2) {
testAll(testArrays[i], testArrays[i + 1]);
testAll(testArrays[i + 1], testArrays[i]);
}
});
it('calculates the binary index', () => {
const testArrays = [
[], [1], [1, 10], [1, 10, 11, 12, 13, 14, 100], [-100, -50, 0, 50, 100], [-100, -14, -13, -12, -11, -10, -1]
];
function testArray(array: number[]) {
function comparator(a: number, b: number) {
return a < b ? -1 : (a > b ? 1 : 0);
}
for (let i = -100; i <= 100; ++i) {
let reference = array.indexOf(i);
let actual = array.binaryIndexOf(i, comparator);
assert.deepStrictEqual(reference, actual);
}
return true;
}
for (let i = 0, l = testArrays.length; i < l; ++i) {
testArray(testArrays[i]);
}
});
it('calculates the lower bound', () => {
const testArrays = [[], [1], [-1, -1, 0, 0, 0, 0, 2, 3, 4, 4, 4, 7, 9, 9, 9]];
function testArray(array: number[], useComparator: boolean) {
function comparator(a: number, b: number) {
return a < b ? -1 : (a > b ? 1 : 0);
}
for (let value = -2; value <= 12; ++value) {
let index = useComparator ? array.lowerBound(value, comparator) : array.lowerBound(value);
assert.isTrue(0 <= index && index <= array.length, 'index is not within bounds');
assert.isTrue(index === 0 || array[index - 1] < value, 'array[index - 1] >= value');
assert.isTrue(index === array.length || array[index] >= value, 'array[index] < value');
}
}
for (let i = 0, l = testArrays.length; i < l; ++i) {
testArray(testArrays[i], false);
testArray(testArrays[i], true);
}
});
it('calculates the upper bound', () => {
const testArrays = [[], [1], [-1, -1, 0, 0, 0, 0, 2, 3, 4, 4, 4, 7, 9, 9, 9]];
function testArray(array: number[], useComparator: boolean) {
function comparator(a: number, b: number) {
return a < b ? -1 : (a > b ? 1 : 0);
}
for (let value = -2; value <= 12; ++value) {
let index = useComparator ? array.upperBound(value, comparator) : array.upperBound(value);
assert.isTrue(0 <= index && index <= array.length, 'index is out of bounds');
assert.isTrue(index === 0 || array[index - 1] <= value, 'array[index - 1] > value');
assert.isTrue(index === array.length || array[index] > value, 'array[index] <= value');
}
}
for (let i = 0, l = testArrays.length; i < l; ++i) {
testArray(testArrays[i], false);
testArray(testArrays[i], true);
}
});
it('q selects', () => {
let testArrays =
[[], [0], [0, 0, 0, 0, 0, 0, 0, 0], [4, 3, 2, 1], [1, 2, 3, 4, 5], [-1, 3, 2, 7, 7, 7, 10, 12, 3, 4, -1, 2]];
function testArray(array: number[]) {
function compare(a: number, b: number) {
return a - b;
}
const sorted = array.slice(0).sort(compare);
const reference = {min: sorted[0], median: sorted[Math.floor(sorted.length / 2)], max: sorted[sorted.length - 1]};
const actual = {
min: array.slice(0).qselect(0),
median: array.slice(0).qselect(Math.floor(array.length / 2)),
max: array.slice(0).qselect(array.length - 1)
};
assert.deepStrictEqual(reference, actual);
}
for (let i = 0, l = testArrays.length; i < l; ++i)
testArray(testArrays[i]);
});
it('sorts ranges', () => {
const testArrays = [[], [1], [2, 1], [6, 4, 2, 7, 10, 15, 1], [10, 44, 3, 6, 56, 66, 10, 55, 32, 56, 2, 5]];
function testArray(array: number[]) {
function comparator(a: number, b: number) {
return a < b ? -1 : (a > b ? 1 : 0);
}
for (let left = 0, l = array.length - 1; left < l; ++left) {
for (let right = left, r = array.length; right < r; ++right) {
for (let first = left; first <= right; ++first) {
for (let count = 1, k = right - first + 1; count <= k; ++count) {
let actual = array.slice(0);
actual.sortRange(comparator, left, right, first, first + count - 1);
assert.deepStrictEqual(array.slice(0, left), actual.slice(0, left), 'left ' + left + ' ' + right + ' ' + count);
assert.deepStrictEqual(
array.slice(right + 1), actual.slice(right + 1), 'right ' + left + ' ' + right + ' ' + count);
let middle = array.slice(left, right + 1);
middle.sort(comparator);
assert.deepStrictEqual(
middle.slice(first - left, first - left + count), actual.slice(first, first + count),
'sorted ' + left + ' ' + right + ' ' + first + ' ' + count);
const actualRest = actual.slice(first + count, right + 1);
actualRest.sort(comparator);
assert.deepStrictEqual(
middle.slice(first - left + count), actualRest,
'unsorted ' + left + ' ' + right + ' ' + first + ' ' + count);
}
}
}
}
}
for (let i = 0, len = testArrays.length; i < len; ++i) {
testArray(testArrays[i]);
}
});
it('sorts natural order', () => {
const testArray = [
'dup', 'a1', 'a4222', 'a91', 'a07', 'dup', 'a7', 'a007', 'abc00', 'abc0',
'abc', 'abcd', 'abc000', 'x10y20z30', 'x9y19z29', 'dup', 'x09y19z29', 'x10y22z23', 'x10y19z43', '1',
'10', '11', 'dup', '2', '2', '2', '555555', '5', '5555', 'dup',
];
for (let i = 0, n = testArray.length; i < n; ++i) {
assert.equal(0, String.naturalOrderComparator(testArray[i], testArray[i]), 'comparing equal strings');
}
testArray.sort(String.naturalOrderComparator);
// Check comparator's transitivity.
for (let i = 0, n = testArray.length; i < n; ++i) {
for (let j = 0; j < n; ++j) {
const a = testArray[i];
const b = testArray[j];
const diff = String.naturalOrderComparator(a, b);
if (diff === 0)
assert.equal(a, b, 'zero diff');
else if (diff < 0)
assert.isTrue(i < j);
else
assert.isTrue(i > j);
}
}
});
it('hashes strings', () => {
const stringA = ' '.repeat(10000);
const stringB = stringA + ' ';
const hashA = String.hashCode(stringA);
assert.isTrue(hashA !== String.hashCode(stringB));
assert.isTrue(isFinite(hashA));
assert.isTrue(hashA + 1 !== hashA);
});
it('trims URLs', () => {
const baseURLDomain = 'www.chromium.org';
const testArray = [
'http://www.chromium.org/foo/bar',
'/foo/bar',
'https://www.CHromium.ORG/BAZ/zoo',
'/BAZ/zoo',
'https://example.com/foo[]',
'example.com/foo[]',
];
for (let i = 0; i < testArray.length; i += 2) {
const url = testArray[i];
const expected = testArray[i + 1];
assert.equal(expected, url.trimURL(baseURLDomain), url);
}
});
it('converts to base64', () => {
const testArray = [
'', '', 'a', 'YQ==', 'bc', 'YmM=', 'def', 'ZGVm', 'ghij', 'Z2hpag==', 'klmno', 'a2xtbm8=', 'pqrstu', 'cHFyc3R1',
String.fromCharCode(0x444, 0x5555, 0x66666, 0x777777), '0YTllZXmmabnnbc='
];
for (let i = 0; i < testArray.length; i += 2) {
const string = testArray[i];
const encodedString = testArray[i + 1];
assert.equal(encodedString, string.toBase64());
}
});
it('trims the middle of strings', () => {
const testArray = [
'', '!', '\uD83D\uDE48A\uD83D\uDE48L\uD83D\uDE48I\uD83D\uDE48N\uD83D\uDE48A\uD83D\uDE48\uD83D\uDE48', 'test'
];
for (let string of testArray) {
for (let maxLength = string.length + 1; maxLength > 0; --maxLength) {
const trimmed = string.trimMiddle(maxLength);
assert.isTrue(trimmed.length <= maxLength);
}
}
});
});
{
"compilerOptions": {
"typeRoots": ["../../../devtools-node-modules/third_party/node_modules/@types"]
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment