mirror of
https://github.com/denoland/deno.git
synced 2025-10-01 06:31:15 +00:00
tests: new typescript WPT runner (#9269)
This commit is contained in:
parent
ecfda65eff
commit
2638aa03a5
16 changed files with 1623 additions and 666 deletions
|
@ -1,35 +0,0 @@
|
|||
## Web Platform Tests
|
||||
|
||||
The WPT are test suites for Web platform specs, like Fetch, WHATWG Streams, or
|
||||
console. Deno is able to run most `.any.js` and `.window.js` web platform tests.
|
||||
|
||||
This directory contains a `wpt.jsonc` file that is used to configure our WPT
|
||||
test runner. You can use this json file to set which WPT suites to run, and
|
||||
which tests we expect to fail (due to bugs or because they are out of scope for
|
||||
Deno).
|
||||
|
||||
To include a new test file to run, add it to the array of test files for the
|
||||
corresponding suite. For example we want to enable
|
||||
`streams/readable-streams/general`. The file would then look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"streams": ["readable-streams/general"]
|
||||
}
|
||||
```
|
||||
|
||||
If you need more configurability over which test cases in a test file of a suite
|
||||
to run, you can use the object representation. In the example below, we
|
||||
configure `streams/readable-streams/general` to expect
|
||||
`ReadableStream can't be constructed with an invalid type` to fail.
|
||||
|
||||
```json
|
||||
{
|
||||
"streams": [
|
||||
{
|
||||
"name": "readable-streams/general",
|
||||
"expectFail": ["ReadableStream can't be constructed with an invalid type"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
|
@ -6,12 +6,9 @@ use deno_core::url;
|
|||
use deno_runtime::deno_fetch::reqwest;
|
||||
use deno_runtime::deno_websocket::tokio_tungstenite;
|
||||
use std::io::{BufRead, Write};
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use tempfile::TempDir;
|
||||
use test_util as util;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
macro_rules! itest(
|
||||
($name:ident {$( $key:ident: $value:expr,)*}) => {
|
||||
|
@ -5193,249 +5190,6 @@ fn denort_direct_use_error() {
|
|||
assert!(!status.success());
|
||||
}
|
||||
|
||||
fn concat_bundle(
|
||||
files: Vec<(PathBuf, String)>,
|
||||
bundle_path: &Path,
|
||||
init: String,
|
||||
) -> String {
|
||||
let bundle_url = url::Url::from_file_path(bundle_path).unwrap().to_string();
|
||||
|
||||
let mut bundle = init.clone();
|
||||
let mut bundle_line_count = init.lines().count() as u32;
|
||||
let mut source_map = sourcemap::SourceMapBuilder::new(Some(&bundle_url));
|
||||
|
||||
// In classic workers, `importScripts()` performs an actual import.
|
||||
// However, we don't implement that function in Deno as we want to enforce
|
||||
// the use of ES6 modules.
|
||||
// To work around this, we:
|
||||
// 1. Define `importScripts()` as a no-op (code below)
|
||||
// 2. Capture its parameter from the source code and add it to the list of
|
||||
// files to concatenate. (see `web_platform_tests()`)
|
||||
bundle.push_str("function importScripts() {}\n");
|
||||
bundle_line_count += 1;
|
||||
|
||||
for (path, text) in files {
|
||||
let path = std::fs::canonicalize(path).unwrap();
|
||||
let url = url::Url::from_file_path(path).unwrap().to_string();
|
||||
let src_id = source_map.add_source(&url);
|
||||
source_map.set_source_contents(src_id, Some(&text));
|
||||
|
||||
for (line_index, line) in text.lines().enumerate() {
|
||||
bundle.push_str(line);
|
||||
bundle.push('\n');
|
||||
source_map.add_raw(
|
||||
bundle_line_count,
|
||||
0,
|
||||
line_index as u32,
|
||||
0,
|
||||
Some(src_id),
|
||||
None,
|
||||
);
|
||||
|
||||
bundle_line_count += 1;
|
||||
}
|
||||
bundle.push('\n');
|
||||
bundle_line_count += 1;
|
||||
}
|
||||
|
||||
let mut source_map_buf: Vec<u8> = vec![];
|
||||
source_map
|
||||
.into_sourcemap()
|
||||
.to_writer(&mut source_map_buf)
|
||||
.unwrap();
|
||||
|
||||
bundle.push_str("//# sourceMappingURL=data:application/json;base64,");
|
||||
let encoded_map = base64::encode(source_map_buf);
|
||||
bundle.push_str(&encoded_map);
|
||||
|
||||
bundle
|
||||
}
|
||||
|
||||
// TODO(lucacasonato): DRY with tsc_config.rs
|
||||
/// Convert a jsonc libraries `JsonValue` to a serde `Value`.
|
||||
fn jsonc_to_serde(j: jsonc_parser::JsonValue) -> serde_json::Value {
|
||||
use jsonc_parser::JsonValue;
|
||||
use serde_json::Value;
|
||||
use std::str::FromStr;
|
||||
match j {
|
||||
JsonValue::Array(arr) => {
|
||||
let vec = arr.into_iter().map(jsonc_to_serde).collect();
|
||||
Value::Array(vec)
|
||||
}
|
||||
JsonValue::Boolean(bool) => Value::Bool(bool),
|
||||
JsonValue::Null => Value::Null,
|
||||
JsonValue::Number(num) => {
|
||||
let number =
|
||||
serde_json::Number::from_str(&num).expect("could not parse number");
|
||||
Value::Number(number)
|
||||
}
|
||||
JsonValue::Object(obj) => {
|
||||
let mut map = serde_json::map::Map::new();
|
||||
for (key, json_value) in obj.into_iter() {
|
||||
map.insert(key, jsonc_to_serde(json_value));
|
||||
}
|
||||
Value::Object(map)
|
||||
}
|
||||
JsonValue::String(str) => Value::String(str),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn web_platform_tests() {
|
||||
use deno_core::serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum WptConfig {
|
||||
Simple(String),
|
||||
#[serde(rename_all = "camelCase")]
|
||||
Options {
|
||||
name: String,
|
||||
expect_fail: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
let text =
|
||||
std::fs::read_to_string(util::tests_path().join("wpt.jsonc")).unwrap();
|
||||
let jsonc = jsonc_parser::parse_to_value(&text).unwrap().unwrap();
|
||||
let config: std::collections::HashMap<String, Vec<WptConfig>> =
|
||||
deno_core::serde_json::from_value(jsonc_to_serde(jsonc)).unwrap();
|
||||
|
||||
for (suite_name, includes) in config.into_iter() {
|
||||
let suite_path = util::wpt_path().join(suite_name);
|
||||
let dir = WalkDir::new(&suite_path)
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.filter(|e| e.file_type().is_file())
|
||||
.filter(|f| {
|
||||
let filename = f.file_name().to_str().unwrap();
|
||||
filename.ends_with(".any.js")
|
||||
|| filename.ends_with(".window.js")
|
||||
|| filename.ends_with(".worker.js")
|
||||
})
|
||||
.filter_map(|f| {
|
||||
let path = f
|
||||
.path()
|
||||
.strip_prefix(&suite_path)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap();
|
||||
for cfg in &includes {
|
||||
match cfg {
|
||||
WptConfig::Simple(name) if path.starts_with(name) => {
|
||||
return Some((f.path().to_owned(), vec![]))
|
||||
}
|
||||
WptConfig::Options { name, expect_fail }
|
||||
if path.starts_with(name) =>
|
||||
{
|
||||
return Some((f.path().to_owned(), expect_fail.to_vec()))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
|
||||
let testharness_path = util::wpt_path().join("resources/testharness.js");
|
||||
let testharness_text = std::fs::read_to_string(&testharness_path)
|
||||
.unwrap()
|
||||
.replace("output:true", "output:false");
|
||||
let testharnessreporter_path =
|
||||
util::tests_path().join("wpt_testharnessconsolereporter.js");
|
||||
let testharnessreporter_text =
|
||||
std::fs::read_to_string(&testharnessreporter_path).unwrap();
|
||||
|
||||
for (test_file_path, expect_fail) in dir {
|
||||
let test_file_text = std::fs::read_to_string(&test_file_path).unwrap();
|
||||
let imports: Vec<(PathBuf, String)> = test_file_text
|
||||
.split('\n')
|
||||
.into_iter()
|
||||
.filter_map(|t| {
|
||||
// Hack: we don't implement `importScripts()`, and instead capture the
|
||||
// parameter in source code; see `concat_bundle()` for more details.
|
||||
if let Some(rest_import_scripts) = t.strip_prefix("importScripts(\"")
|
||||
{
|
||||
if let Some(import_path) = rest_import_scripts.strip_suffix("\");")
|
||||
{
|
||||
// The code in `testharness.js` silences the test outputs.
|
||||
if import_path != "/resources/testharness.js" {
|
||||
return Some(import_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
t.strip_prefix("// META: script=")
|
||||
})
|
||||
.map(|s| {
|
||||
let s = if s == "/resources/WebIDLParser.js" {
|
||||
"/resources/webidl2/lib/webidl2.js"
|
||||
} else {
|
||||
s
|
||||
};
|
||||
if s.starts_with('/') {
|
||||
util::wpt_path().join(format!(".{}", s))
|
||||
} else {
|
||||
test_file_path.parent().unwrap().join(s)
|
||||
}
|
||||
})
|
||||
.map(|path| {
|
||||
let text = std::fs::read_to_string(&path).unwrap();
|
||||
(path, text)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut variants: Vec<&str> = test_file_text
|
||||
.split('\n')
|
||||
.into_iter()
|
||||
.filter_map(|t| t.strip_prefix("// META: variant="))
|
||||
.collect();
|
||||
|
||||
if variants.is_empty() {
|
||||
variants.push("");
|
||||
}
|
||||
|
||||
for variant in variants {
|
||||
let mut files = Vec::with_capacity(3 + imports.len());
|
||||
files.push((testharness_path.clone(), testharness_text.clone()));
|
||||
files.push((
|
||||
testharnessreporter_path.clone(),
|
||||
testharnessreporter_text.clone(),
|
||||
));
|
||||
files.extend(imports.clone());
|
||||
files.push((test_file_path.clone(), test_file_text.clone()));
|
||||
|
||||
let mut file = tempfile::Builder::new()
|
||||
.prefix("wpt-bundle-")
|
||||
.suffix(".js")
|
||||
.rand_bytes(5)
|
||||
.tempfile()
|
||||
.unwrap();
|
||||
|
||||
let bundle = concat_bundle(files, file.path(), "".to_string());
|
||||
file.write_all(bundle.as_bytes()).unwrap();
|
||||
|
||||
let child = util::deno_cmd()
|
||||
.current_dir(test_file_path.parent().unwrap())
|
||||
.arg("run")
|
||||
.arg("--location")
|
||||
.arg(&format!("http://web-platform-tests/?{}", variant))
|
||||
.arg("-A")
|
||||
.arg(file.path())
|
||||
.arg(deno_core::serde_json::to_string(&expect_fail).unwrap())
|
||||
.arg("--quiet")
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let output = child.wait_with_output().unwrap();
|
||||
if !output.status.success() {
|
||||
file.keep().unwrap();
|
||||
}
|
||||
assert!(output.status.success());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_resolve_dns() {
|
||||
use std::collections::BTreeMap;
|
||||
|
|
|
@ -1,250 +0,0 @@
|
|||
{
|
||||
"streams": [
|
||||
// "piping/abort",
|
||||
// "piping/close-propagation-backward",
|
||||
// "piping/close-propagation-forward",
|
||||
// "piping/error-propagation-backward",
|
||||
// "piping/error-propagation-forward",
|
||||
"piping/flow-control",
|
||||
// "piping/general",
|
||||
"piping/multiple-propagation",
|
||||
"piping/pipe-through",
|
||||
"piping/then-interception",
|
||||
// "piping/throwing-options",
|
||||
// "piping/transform-streams",
|
||||
"queuing-strategies.any",
|
||||
// "readable-byte-streams",
|
||||
// "readable-streams/async-iterator",
|
||||
// "readable-streams/bad-strategies",
|
||||
// "readable-streams/bad-underlying-source",
|
||||
// "readable-streams/cancel",
|
||||
// "readable-streams/constructor",
|
||||
"readable-streams/count-queuing-strategy-integration",
|
||||
"readable-streams/default-reader",
|
||||
"readable-streams/floating-point-total-queue-size",
|
||||
"readable-streams/garbage-collection",
|
||||
"readable-streams/general",
|
||||
{
|
||||
"name": "readable-streams/patched-global",
|
||||
"expectFail": [
|
||||
"ReadableStream async iterator should use the original values of getReader() and ReadableStreamDefaultReader methods"
|
||||
]
|
||||
},
|
||||
"readable-streams/reentrant-strategies",
|
||||
"readable-streams/tee",
|
||||
// "readable-streams/templated",
|
||||
"transform-streams/backpressure",
|
||||
"transform-streams/errors",
|
||||
"transform-streams/flush",
|
||||
"transform-streams/general",
|
||||
"transform-streams/lipfuzz",
|
||||
// "transform-streams/patched-global",
|
||||
"transform-streams/properties",
|
||||
"transform-streams/reentrant-strategies",
|
||||
"transform-streams/strategies",
|
||||
// "transform-streams/terminate",
|
||||
// "writable-streams/aborting",
|
||||
// "writable-streams/bad-strategies",
|
||||
"writable-streams/bad-underlying-sinks",
|
||||
"writable-streams/byte-length-queuing-strategy",
|
||||
// "writable-streams/close",
|
||||
// "writable-streams/constructor",
|
||||
"writable-streams/count-queuing-strategy",
|
||||
"writable-streams/error",
|
||||
"writable-streams/floating-point-total-queue-size",
|
||||
"writable-streams/general",
|
||||
"writable-streams/properties",
|
||||
"writable-streams/reentrant-strategy",
|
||||
"writable-streams/start",
|
||||
"writable-streams/write"
|
||||
],
|
||||
"encoding": [
|
||||
"api-basics",
|
||||
"api-invalid-label",
|
||||
"api-replacement-encodings",
|
||||
"api-surrogates-utf8",
|
||||
// TODO(lucacasonato): enable encodeInto. We have a bug in implementaiton.
|
||||
// {
|
||||
// "name": "encodeInto",
|
||||
// "expectFail": [
|
||||
// "encodeInto() and a detached output buffer"
|
||||
// ]
|
||||
// },
|
||||
// "encodeInto",
|
||||
// TODO(lucacasonato): enable when we support iso-2022-jp
|
||||
// "iso-2022-jp-decoder",
|
||||
// TODO(lucacasonato): uses XMLHttpRequest unnecessarily. should be fixed upstream before enabling
|
||||
// "replacement-encodings",
|
||||
"textdecoder-byte-order-marks",
|
||||
{
|
||||
"name": "textdecoder-copy",
|
||||
"expectFail": [
|
||||
// TODO(lucacasonato): enable when we have stream support
|
||||
"Modify buffer after passing it in (ArrayBuffer)",
|
||||
"Modify buffer after passing it in (SharedArrayBuffer)"
|
||||
]
|
||||
},
|
||||
"textdecoder-fatal-single-byte",
|
||||
"textdecoder-fatal.",
|
||||
"textdecoder-ignorebom",
|
||||
{
|
||||
"name": "textdecoder-labels",
|
||||
"expectFail": [
|
||||
"cseucpkdfmtjapanese => EUC-JP",
|
||||
"euc-jp => EUC-JP",
|
||||
"x-euc-jp => EUC-JP",
|
||||
"csiso2022jp => ISO-2022-JP",
|
||||
"iso-2022-jp => ISO-2022-JP",
|
||||
"csshiftjis => Shift_JIS",
|
||||
"ms932 => Shift_JIS",
|
||||
"ms_kanji => Shift_JIS",
|
||||
"shift-jis => Shift_JIS",
|
||||
"shift_jis => Shift_JIS",
|
||||
"sjis => Shift_JIS",
|
||||
"windows-31j => Shift_JIS",
|
||||
"x-sjis => Shift_JIS",
|
||||
"cseuckr => EUC-KR",
|
||||
"csksc56011987 => EUC-KR",
|
||||
"euc-kr => EUC-KR",
|
||||
"iso-ir-149 => EUC-KR",
|
||||
"korean => EUC-KR",
|
||||
"ks_c_5601-1987 => EUC-KR",
|
||||
"ks_c_5601-1989 => EUC-KR",
|
||||
"ksc5601 => EUC-KR",
|
||||
"ksc_5601 => EUC-KR",
|
||||
"windows-949 => EUC-KR",
|
||||
"x-user-defined => x-user-defined"
|
||||
]
|
||||
},
|
||||
// TODO(lucacasonato): enable when we have stream support
|
||||
// "textdecoder-streaming",
|
||||
"textdecoder-utf16-surrogates",
|
||||
{
|
||||
"name": "textencoder-constructor-non-utf",
|
||||
"expectFail": [
|
||||
"Encoding argument supported for decode: EUC-JP",
|
||||
"Encoding argument supported for decode: ISO-2022-JP",
|
||||
"Encoding argument supported for decode: Shift_JIS",
|
||||
"Encoding argument supported for decode: EUC-KR",
|
||||
"Encoding argument supported for decode: x-user-defined"
|
||||
]
|
||||
},
|
||||
"textencoder-utf16-surrogates",
|
||||
"legacy-mb-schinese"
|
||||
// TODO(lucacasonato): uses XMLHttpRequest unnecessarily. should be fixed upstream before enabling
|
||||
// "unsupported-encodings",
|
||||
],
|
||||
"dom": [
|
||||
"abort/event"
|
||||
],
|
||||
"hr-time": [
|
||||
"monotonic-clock"
|
||||
],
|
||||
"html": [
|
||||
"webappapis/microtask-queuing/queue-microtask-exceptions.any",
|
||||
"webappapis/microtask-queuing/queue-microtask.any",
|
||||
"webappapis/timers"
|
||||
],
|
||||
"user-timing": [
|
||||
"clear_all_marks",
|
||||
"clear_all_measures",
|
||||
"clear_non_existent_mark",
|
||||
"clear_non_existent_measure",
|
||||
"clear_one_mark",
|
||||
"clear_one_measure",
|
||||
"entry_type",
|
||||
"mark-entry-constructor",
|
||||
"mark-errors",
|
||||
"mark-measure-return-objects",
|
||||
"mark.any",
|
||||
"measure_syntax_err",
|
||||
"measure-l3",
|
||||
"structured-serialize-detail",
|
||||
"user_timing_exists"
|
||||
],
|
||||
"wasm": [
|
||||
"jsapi/constructor/compile",
|
||||
"jsapi/constructor/multi-value",
|
||||
"jsapi/constructor/toStringTag",
|
||||
"jsapi/constructor/validate",
|
||||
"jsapi/global/constructor",
|
||||
"jsapi/global/toString",
|
||||
"jsapi/global/value-get-set",
|
||||
"jsapi/global/valueOf",
|
||||
"jsapi/instance/toString",
|
||||
"jsapi/instance/constructor-caching",
|
||||
"jsapi/memory/toString",
|
||||
"jsapi/module/constructor",
|
||||
"jsapi/module/customSections",
|
||||
"jsapi/module/exports",
|
||||
"jsapi/module/imports",
|
||||
"jsapi/module/toString",
|
||||
"jsapi/table/get-set",
|
||||
"jsapi/table/toString",
|
||||
"webapi/body",
|
||||
"webapi/invalid-args",
|
||||
"webapi/rejected-arg",
|
||||
"webapi/status",
|
||||
"webapi/create_multiple_memory",
|
||||
"create_multiple_memory"
|
||||
//FAILING TESTS
|
||||
// "jsapi/constructor/instantiate-bad-imports",
|
||||
// "jsapi/constructor/instantiate",
|
||||
// "jsapi/global/type",
|
||||
// "jsapi/instance/constructor-bad-imports",
|
||||
// "jsapi/instance/constructor",
|
||||
// "jsapi/instance/exports",
|
||||
// "jsapi/memory/buffer",
|
||||
// "jsapi/memory/constructor-shared",
|
||||
// "jsapi/memory/constructor-types",
|
||||
// "jsapi/memory/constructor",
|
||||
// "jsapi/memory/grow",
|
||||
// "jsapi/memory/type",
|
||||
// "jsapi/table/constructor-types",
|
||||
// "jsapi/table/constructor",
|
||||
// "jsapi/table/grow-reftypes",
|
||||
// "jsapi/table/grow",
|
||||
// "jsapi/table/length",
|
||||
// "jsapi/idlharness",
|
||||
// "jsapi/instance",
|
||||
// "jsapi/prototypes",
|
||||
// "serialization/arraybuffer/transfer"
|
||||
// "serialization/module/nested-worker-success",
|
||||
// "serialization/module/serialization-via-idb",
|
||||
// "serialization/module/serialization-via-notifications-api",
|
||||
// "webapi/abort",
|
||||
// "webapi/contenttype",
|
||||
// "webapi/empty-body",
|
||||
// "webapi/historical",
|
||||
// "webapi/idlharness",
|
||||
// "webapi/instantiateStreaming-bad-imports",
|
||||
// "webapi/instantiateStreaming",
|
||||
// "webapi/invalid-code",
|
||||
// "webapi/origin",
|
||||
],
|
||||
"console": [
|
||||
"console-is-a-namespace",
|
||||
"console-label-conversion",
|
||||
"console-namespace-object-class-string",
|
||||
"console-tests-historical"
|
||||
],
|
||||
"WebCryptoApi": [
|
||||
"getRandomValues"
|
||||
],
|
||||
"WebIDL": [
|
||||
"ecmascript-binding/es-exceptions/DOMException-constants",
|
||||
"ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype",
|
||||
"ecmascript-binding/es-exceptions/DOMException-constructor-behavior",
|
||||
{
|
||||
"name": "ecmascript-binding/es-exceptions/DOMException-custom-bindings",
|
||||
"expectFail": [
|
||||
// TODO(kt3k): Enable this test.
|
||||
// We can pass this test by using Object.setPrototypeOf(...) instead of
|
||||
// class...extends, but that causes a problem in printing of uncaught
|
||||
// DOMException. We might need to modify how to print uncaught error in
|
||||
// `//core/error.rs`.
|
||||
"does not inherit from Error: class-side"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
const noColor = globalThis.Deno?.noColor ?? true;
|
||||
const enabled = !noColor;
|
||||
|
||||
function code(open, close) {
|
||||
return {
|
||||
open: `\x1b[${open.join(";")}m`,
|
||||
close: `\x1b[${close}m`,
|
||||
regexp: new RegExp(`\\x1b\\[${close}m`, "g"),
|
||||
};
|
||||
}
|
||||
|
||||
function run(str, code) {
|
||||
return enabled
|
||||
? `${code.open}${str.replace(code.regexp, code.open)}${code.close}`
|
||||
: str;
|
||||
}
|
||||
|
||||
function red(str) {
|
||||
return run(str, code([31], 39));
|
||||
}
|
||||
|
||||
export function green(str) {
|
||||
return run(str, code([32], 39));
|
||||
}
|
||||
|
||||
export function yellow(str) {
|
||||
return run(str, code([33], 39));
|
||||
}
|
||||
|
||||
const testResults = [];
|
||||
const testsExpectFail = JSON.parse(Deno.args[0]);
|
||||
function shouldExpectFail(name) {
|
||||
if (testsExpectFail.includes(name)) return true;
|
||||
for (const expectFail of testsExpectFail) {
|
||||
if (name.startsWith(expectFail)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
window.add_result_callback(({ message, name, stack, status }) => {
|
||||
const expectFail = shouldExpectFail(name);
|
||||
testResults.push({
|
||||
name,
|
||||
passed: status === 0,
|
||||
expectFail,
|
||||
message,
|
||||
stack,
|
||||
});
|
||||
let simpleMessage = `test ${name} ... `;
|
||||
switch (status) {
|
||||
case 0:
|
||||
if (expectFail) {
|
||||
simpleMessage += red("ok (expected fail)");
|
||||
} else {
|
||||
simpleMessage += green("ok");
|
||||
if (Deno.args[1] == "--quiet") {
|
||||
// don't print `ok` tests if --quiet is enabled
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (expectFail) {
|
||||
simpleMessage += yellow("failed (expected)");
|
||||
} else {
|
||||
simpleMessage += red("failed");
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (expectFail) {
|
||||
simpleMessage += yellow("failed (expected)");
|
||||
} else {
|
||||
simpleMessage += red("failed (timeout)");
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (expectFail) {
|
||||
simpleMessage += yellow("failed (expected)");
|
||||
} else {
|
||||
simpleMessage += red("failed (incomplete)");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
console.log(simpleMessage);
|
||||
});
|
||||
|
||||
window.add_completion_callback((tests, harnessStatus) => {
|
||||
const failed = testResults.filter((t) => !t.expectFail && !t.passed);
|
||||
const expectedFailedButPassed = testResults.filter((t) =>
|
||||
t.expectFail && t.passed
|
||||
);
|
||||
const expectedFailedButPassedCount = expectedFailedButPassed.length;
|
||||
const failedCount = failed.length + expectedFailedButPassedCount;
|
||||
const expectedFailedAndFailedCount = testResults.filter((t) =>
|
||||
t.expectFail && !t.passed
|
||||
).length;
|
||||
const totalCount = testResults.length;
|
||||
const passedCount = totalCount - failedCount - expectedFailedAndFailedCount;
|
||||
|
||||
if (failed.length > 0) {
|
||||
console.log(`\nfailures:`);
|
||||
}
|
||||
for (const result of failed) {
|
||||
console.log(
|
||||
`\n${result.name}\n${result.message}\n${result.stack}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (failed.length > 0) {
|
||||
console.log(`\nfailures:\n`);
|
||||
}
|
||||
for (const result of failed) {
|
||||
console.log(` ${result.name}`);
|
||||
}
|
||||
if (expectedFailedButPassedCount > 0) {
|
||||
console.log(`\nexpected failures that passed:\n`);
|
||||
}
|
||||
for (const result of expectedFailedButPassed) {
|
||||
console.log(` ${result.name}`);
|
||||
}
|
||||
console.log(
|
||||
`\ntest result: ${
|
||||
failedCount > 0 ? red("failed") : green("ok")
|
||||
}. ${passedCount} passed; ${failedCount} failed; ${expectedFailedAndFailedCount} expected failure; total ${totalCount}\n`,
|
||||
);
|
||||
|
||||
Deno.exit(failedCount > 0 ? 1 : 0);
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue