diff --git a/.gitignore b/.gitignore index 956aa0c381..57710edc6b 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,7 @@ /test/mozilla/data /test/test262/data /test/test262/harness -/test/wasm-js +/test/wasm-js/data /test/wasm-spec-tests/tests /test/wasm-spec-tests/tests.tar.gz /third_party/* diff --git a/DEPS b/DEPS index 37b9b8278f..ed1231359b 100644 --- a/DEPS +++ b/DEPS @@ -84,8 +84,8 @@ deps = { Var('chromium_url') + '/chromium/src/tools/clang.git' + '@' + 'a245b955fe9cd620081ed267fae303c88d033fef', 'v8/tools/luci-go': Var('chromium_url') + '/chromium/src/tools/luci-go.git' + '@' + '445d7c4b6a4f10e188edb395b132e3996b127691', - 'v8/test/wasm-js': - Var('chromium_url') + '/external/github.com/WebAssembly/spec.git' + '@' + 'db9cd40808a90ecc5f4a23e88fb375c8f60b8d52', + 'v8/test/wasm-js/data': + Var('chromium_url') + '/external/github.com/WebAssembly/spec.git' + '@' + 'cf67a477d2d802329640ef4e6461e4c180d8f910', } recursedeps = [ diff --git a/test/BUILD.gn b/test/BUILD.gn index 45e1b34032..70c8b51fa3 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -16,6 +16,7 @@ group("gn_all") { "mozilla:v8_mozilla", "preparser:v8_preparser", "test262:v8_test262", + "wasm-js:v8_wasm_js", "wasm-spec-tests:v8_wasm_spec_tests", "webkit:v8_webkit", ] @@ -80,6 +81,7 @@ group("v8_bot_default") { "mkgrokdump:mkgrokdump", "preparser:v8_preparser", "unittests:unittests", + "wasm-js:v8_wasm_js", "wasm-spec-tests:v8_wasm_spec_tests", "webkit:v8_webkit", ] @@ -99,6 +101,7 @@ group("v8_default") { "mkgrokdump:mkgrokdump", "preparser:v8_preparser", "unittests:unittests", + "wasm-js:v8_wasm_js", "wasm-spec-tests:v8_wasm_spec_tests", ] } diff --git a/test/mjsunit/wasm/jsapi-harness.js b/test/mjsunit/wasm/jsapi-harness.js deleted file mode 100644 index d827b67570..0000000000 --- a/test/mjsunit/wasm/jsapi-harness.js +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2017 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// TODO(eholk): Once we have stable test IDs, use those as the key instead. -// See https://github.com/WebAssembly/spec/issues/415 -// -// Flags: --expose-wasm --allow-natives-syntax - -const known_failures = { - // Enter failing tests like follows: - // "'WebAssembly.Instance.prototype.exports' accessor property": - // 'https://bugs.chromium.org/p/v8/issues/detail?id=5507', -}; - -let failures = []; -let unexpected_successes = []; - -let last_promise = new Promise((resolve, reject) => { resolve(); }); - -function test(func, description) { - let maybeErr; - try { func(); } - catch(e) { maybeErr = e; } - if (typeof maybeErr !== 'undefined') { - var known = ""; - if (known_failures[description]) { - known = " (known)"; - } - print(`${description}: FAIL${known}. ${maybeErr}`); - failures.push(description); - } else { - if (known_failures[description]) { - unexpected_successes.push(description); - } - print(`${description}: PASS.`); - } -} - -function promise_test(func, description) { - last_promise = last_promise.then(func) - .then(_ => { - if (known_failures[description]) { - unexpected_successes.push(description); - } - print(`${description}: PASS.`); - }) - .catch(err => { - var known = ""; - if (known_failures[description]) { - known = " (known)"; - } - print(`${description}: FAIL${known}. ${err}`); - failures.push(description); - }); -} - -let assert_true = assertEquals.bind(null, true); -let assert_false = assertEquals.bind(null, false); - -function same_value(x, y) { - if (y !== y) { - // NaN case - return x!==x; - } - if (x === 0 && y === 0) { - // Distinguish +0 and -0 - return 1/x === 1/y; - } - return x === y; -} - -let assert_equals = function(expected, found, description) { - if (typeof found != typeof expected) { - assert_true(false, "assert_equals", description, - "expected (" + typeof expected + ") ${expected} but got (" + - typeof found + ") ${found}", {expected:expected, found:found}); - } - assert_true(same_value(found, expected), "assert_equals", description, - "expected ${expected} but got ${found}", - {expected:expected, found:found}); -} - -let assert_not_equals = function(expected, found, description) { - assert_true(!same_value(found, expected), "assert_not_equals", description, - "got disallowed value ${found}", {found:found}); -} - -function assert_unreached(description) { - throw new Error(`unreachable:\n${description}`); -} - -function assertErrorMessage(f, ctor, test) { - try { f(); } - catch (e) { - assert_true(e instanceof ctor, "expected exception " + ctor.name + ", got " + e); - return; - } - assert_true(false, "expected exception " + ctor.name + ", no exception thrown"); -}; - -load("test/wasm-js/test/harness/wasm-constants.js"); -load("test/wasm-js/test/harness/wasm-module-builder.js"); -load("test/wasm-js/test/js-api/jsapi.js"); - -assertPromiseResult(last_promise, _ => { - if (failures.length > 0) { - let unexpected = false; - print("Some tests FAILED:"); - for (let i in failures) { - if (known_failures[failures[i]]) { - print(` ${failures[i]} [KNOWN: ${known_failures[failures[i]]}]`); - } else { - print(` ${failures[i]}`); - unexpected = true; - } - } - if (unexpected_successes.length > 0) { - unexpected = true; - print(""); - print("Unexpected successes:"); - for(let i in unexpected_successes) { - print(` ${unexpected_successes[i]}`); - } - print("Some tests SUCCEEDED but were known failures. If you've fixed " + - "the bug, please remove the test from the known failures list.") - } - if (unexpected) { - print("\n"); - print(" #############################################################"); - print(" # #"); - print(" # Unexpected outcome. Did you forget to run 'gclient sync'? #"); - print(" # #"); - print(" #############################################################"); - print("\n"); - assertUnreachable("Unexpected outcome"); - } - } -}); diff --git a/test/wasm-js/BUILD.gn b/test/wasm-js/BUILD.gn new file mode 100644 index 0000000000..2f7b8c31fb --- /dev/null +++ b/test/wasm-js/BUILD.gn @@ -0,0 +1,17 @@ +# Copyright 2018 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +group("v8_wasm_js") { + testonly = true + + data_deps = [ + "../..:d8", + "../../tools:v8_testrunner", + ] + + data = [ + "./", + "../mjsunit/mjsunit.js", + ] +} diff --git a/test/wasm-js/LICENSE.testharness b/test/wasm-js/LICENSE.testharness new file mode 100644 index 0000000000..45896e6be2 --- /dev/null +++ b/test/wasm-js/LICENSE.testharness @@ -0,0 +1,30 @@ +W3C 3-clause BSD License + +http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of works must retain the original copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the original copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of the W3C nor the names of its contributors may be + used to endorse or promote products derived from this work without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/test/wasm-js/testcfg.py b/test/wasm-js/testcfg.py new file mode 100644 index 0000000000..b0763e008a --- /dev/null +++ b/test/wasm-js/testcfg.py @@ -0,0 +1,74 @@ +# Copyright 2018 the V8 project 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 + +from testrunner.local import testsuite +from testrunner.objects import testcase + +ANY_JS = ".any.js" +WPT_ROOT = "/wasm/jsapi/" +META_SCRIPT_REGEXP = re.compile(r"META:\s*script=(.*)") + +class TestSuite(testsuite.TestSuite): + def __init__(self, *args, **kwargs): + super(TestSuite, self).__init__(*args, **kwargs) + self.testroot = os.path.join(self.root, "data", "test", "js-api") + self.mjsunit_js = os.path.join(os.path.dirname(self.root), "mjsunit", + "mjsunit.js") + + def ListTests(self): + tests = [] + for dirname, dirs, files in os.walk(self.testroot): + for dotted in [x for x in dirs if x.startswith(".")]: + dirs.remove(dotted) + dirs.sort() + files.sort() + for filename in files: + if (filename.endswith(ANY_JS)): + fullpath = os.path.join(dirname, filename) + relpath = fullpath[len(self.testroot) + 1 : -len(ANY_JS)] + testname = relpath.replace(os.path.sep, "/") + test = self._create_test(testname) + tests.append(test) + return tests + + def _test_class(self): + return TestCase + + +class TestCase(testcase.D8TestCase): + def _get_files_params(self): + files = [os.path.join(self.suite.mjsunit_js), + os.path.join(self.suite.root, "testharness.js")] + + source = self.get_source() + for script in META_SCRIPT_REGEXP.findall(source): + if script.startswith(WPT_ROOT): + # Matched an absolute path, strip the root and replace it with our + # local root. + script = os.path.join(self.suite.testroot, script[len(WPT_ROOT):]) + elif not script.startswith("/"): + # Matched a relative path, prepend this test's directory. + thisdir = os.path.dirname(self._get_source_path()) + script = os.path.join(thisdir, script) + else: + raise Exception("Unexpected absolute path for script: \"%s\"" % script); + + files.append(script) + + files.extend([ + self._get_source_path(), + os.path.join(self.suite.root, "testharness-after.js") + ]) + return files + + def _get_source_path(self): + # All tests are named `path/name.any.js` + return os.path.join(self.suite.testroot, self.path + ANY_JS) + + +def GetSuite(*args, **kwargs): + return TestSuite(*args, **kwargs) diff --git a/test/wasm-js/testharness-after.js b/test/wasm-js/testharness-after.js new file mode 100644 index 0000000000..9520be1c9b --- /dev/null +++ b/test/wasm-js/testharness-after.js @@ -0,0 +1,16 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Uses lastPromise defined in testharness.js + +assertPromiseResult(lastPromise, _ => { + if (failures.length > 0) { + let message = 'Some tests FAILED:\n'; + for (const failure of failures) { + message += ` ${failure}\n`; + } + + failWithMessage(message); + } +}); diff --git a/test/wasm-js/testharness.js b/test/wasm-js/testharness.js new file mode 100644 index 0000000000..ecaf0b82d9 --- /dev/null +++ b/test/wasm-js/testharness.js @@ -0,0 +1,142 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Implementations of some functions from testharness.js +// See https://github.com/web-platform-tests/wpt/blob/master/resources/testharness.js +// Licensed as follows: +// +// Distributed under both the W3C Test Suite License [1] and the W3C +// 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the +// policies and contribution forms [3]. +// [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license +// [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license +// [3] http://www.w3.org/2004/10/27-testcases + +const failures = []; + +let lastPromise = Promise.resolve(); + +function test(func, description) { + let maybeErr; + try { func({unreached_func: assert_unreached}); } + catch(e) { maybeErr = e; } + if (typeof maybeErr !== 'undefined') { + console.log(`${description}: FAIL. ${maybeErr}`); + failures.push(description); + } else { + console.log(`${description}: PASS.`); + } +} + +function promise_test(func, description) { + lastPromise = lastPromise.then(func) + .then(_ => { + console.log(`${description}: PASS.`); + }) + .catch(err => { + console.log(`${description}: FAIL. ${err}`); + failures.push(description); + }); +} + +const assert_true = assertEquals.bind(null, true); +const assert_false = assertEquals.bind(null, false); + +function same_value(x, y) { + if (y !== y) { + // NaN case + return x!==x; + } + if (x === 0 && y === 0) { + // Distinguish +0 and -0 + return 1/x === 1/y; + } + return x === y; +} + +function assert_equals(expected, found, description) { + if (typeof found != typeof expected) { + assert_true(false, "assert_equals", description, + "expected (" + typeof expected + ") ${expected} but got (" + + typeof found + ") ${found}", {expected:expected, found:found}); + } + assert_true(same_value(found, expected), "assert_equals", description, + "expected ${expected} but got ${found}", + {expected:expected, found:found}); +} + +function assert_not_equals(expected, found, description) { + assert_true(!same_value(found, expected), "assert_not_equals", description, + "got disallowed value ${found}", {found:found}); +} + +function assert_array_equals(actual, expected, description) { + assert_true( + typeof actual === 'object' && actual !== null && 'length' in actual, + 'assert_array_equals', description, 'value is ${actual}, expected array', + {actual: actual}); + assert_true( + actual.length === expected.length, 'assert_array_equals', description, + 'lengths differ, expected ${expected} got ${actual}', + {expected: expected.length, actual: actual.length}); + + for (let i = 0; i < actual.length; i++) { + assert_true( + actual.hasOwnProperty(i) === expected.hasOwnProperty(i), + 'assert_array_equals', description, + 'property ${i}, property expected to be ${expected} but was ${actual}', + { + i: i, + expected: expected.hasOwnProperty(i) ? 'present' : 'missing', + actual: actual.hasOwnProperty(i) ? 'present' : 'missing' + }); + assert_true( + same_value(expected[i], actual[i]), 'assert_array_equals', description, + 'property ${i}, expected ${expected} but got ${actual}', + {i: i, expected: expected[i], actual: actual[i]}); + } +} + +function assert_unreached(description) { + throw new Error(`unreachable:\n${description}`); +} + +function format_value(s) { + // TODO + try { + return String(s); + } catch(e) { + return ``; + } +} + +function promise_rejects(test, expected, promise, description) { + return promise + .then(() => assert_unreached('Should have rejected: ' + description)) + .catch(function(e) { + assert_throws(expected, function() { + throw e; + }, description); + }); +} + +function assert_class_string(object, class_string, description) { + assert_equals( + {}.toString.call(object), '[object ' + class_string + ']', description); +} + +function assert_throws(code, func, description) { + try { + func(); + } catch (e) { + assert_true(e.name === code.name, "expected exception " + code.name + ", got " + e.name); + return; + } + assert_true(false, "expected exception " + code.name + ", no exception thrown"); +} + +function setup(func) { + // TODO need to do anything fancier here? + func(); +} diff --git a/test/wasm-js/wasm-js.status b/test/wasm-js/wasm-js.status new file mode 100644 index 0000000000..e2b06903c0 --- /dev/null +++ b/test/wasm-js/wasm-js.status @@ -0,0 +1,18 @@ +# Copyright 2018 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[ +[ALWAYS, { + # https://bugs.chromium.org/p/v8/issues/detail?id=8319 + 'interface': [FAIL], + 'memory/grow': [FAIL], + 'memory/constructor': [FAIL], + 'table/grow': [FAIL], + 'table/constructor': [FAIL], + 'table/get-set': [FAIL], + 'module/customSections': [FAIL], + 'global/constructor': [FAIL], + 'global/value-get-set': [FAIL], +}] # ALWAYS +] diff --git a/tools/testrunner/base_runner.py b/tools/testrunner/base_runner.py index 776c6de052..5c71cb33ee 100644 --- a/tools/testrunner/base_runner.py +++ b/tools/testrunner/base_runner.py @@ -51,6 +51,7 @@ TEST_MAP = { "inspector", "webkit", "mkgrokdump", + "wasm-js", "fuzzer", "message", "preparser", @@ -65,6 +66,7 @@ TEST_MAP = { "wasm-spec-tests", "inspector", "mkgrokdump", + "wasm-js", "fuzzer", "message", "preparser", diff --git a/tools/wasm/update-wasm-spec-tests.sh b/tools/wasm/update-wasm-spec-tests.sh index 92aaa8fd3c..9189c814b9 100755 --- a/tools/wasm/update-wasm-spec-tests.sh +++ b/tools/wasm/update-wasm-spec-tests.sh @@ -27,16 +27,16 @@ mkdir ${SPEC_TEST_DIR}/tmp ./tools/dev/gm.py x64.release d8 -cd ${V8_DIR}/test/wasm-js/interpreter +cd ${V8_DIR}/test/wasm-js/data/interpreter # The next step requires that ocaml is installed. See the README.md in -# ${V8_DIR}/test/wasm-js/interpreter/. +# ${V8_DIR}/test/wasm-js/data/interpreter/. make clean all -cd ${V8_DIR}/test/wasm-js/test/core +cd ${V8_DIR}/test/wasm-js/data/test/core -./run.py --wasm ${V8_DIR}/test/wasm-js/interpreter/wasm --js ${V8_DIR}/out/x64.release/d8 --out ${SPEC_TEST_DIR}/tmp +./run.py --wasm ${V8_DIR}/test/wasm-js/data/interpreter/wasm --js ${V8_DIR}/out/x64.release/d8 --out ${SPEC_TEST_DIR}/tmp cp ${SPEC_TEST_DIR}/tmp/*.js ${SPEC_TEST_DIR}/tests/ rm -rf ${SPEC_TEST_DIR}/tmp