diff --git a/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini b/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini index 62c2d998e8f..68bf21994e8 100644 --- a/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini +++ b/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini @@ -4,7 +4,7 @@ expected: TIMEOUT [Opening a blob URL in a new window immediately before revoking it works.] - expected: FAIL + expected: TIMEOUT [Fetching a blob URL immediately before revoking it works in an iframe.] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.https.any.js.ini b/tests/wpt/metadata-layout-2020/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.https.any.js.ini index da98e91258d..5e414c7aa15 100644 --- a/tests/wpt/metadata-layout-2020/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.https.any.js.ini +++ b/tests/wpt/metadata-layout-2020/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.https.any.js.ini @@ -2,7 +2,73 @@ [setup - define tests] expected: FAIL + [X25519 key derivation checks for all-zero value result with a key of order 0] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order 1] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order 8] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order p-1 (order 2)] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order p (=0, order 4)] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order 0] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order 1] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order p-1 (order 2)] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order p (=0, order 4)] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)] + expected: FAIL + [cfrg_curves_bits.https.any.worker.html] [setup - define tests] expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order 0] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order 1] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order 8] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order p-1 (order 2)] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order p (=0, order 4)] + expected: FAIL + + [X25519 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order 0] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order 1] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order p-1 (order 2)] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order p (=0, order 4)] + expected: FAIL + + [X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.https.any.js.ini b/tests/wpt/metadata-layout-2020/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.https.any.js.ini index b1dd5e83f57..ce42d7e5f4f 100644 --- a/tests/wpt/metadata-layout-2020/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.https.any.js.ini +++ b/tests/wpt/metadata-layout-2020/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.https.any.js.ini @@ -2,7 +2,19 @@ [setup - define tests] expected: FAIL + [Key derivation using a X25519 generated keys.] + expected: FAIL + + [Key derivation using a X448 generated keys.] + expected: FAIL + [cfrg_curves_keys.https.any.html] [setup - define tests] expected: FAIL + + [Key derivation using a X25519 generated keys.] + expected: FAIL + + [Key derivation using a X448 generated keys.] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js.ini b/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js.ini index 1b64e42417f..c1b505a94d6 100644 --- a/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js.ini +++ b/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js.ini @@ -593,6 +593,144 @@ [Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign, sign\])] expected: FAIL + [Bad usages: importKey(raw, {name: Ed25519}, true, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, deriveBits\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed25519}, true, [verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed25519}, false, [verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed25519}, true, [verify, verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed25519}, false, [verify, verify\])] + expected: FAIL + [okp_importKey_failures_Ed25519.https.any.html] [Bad usages: importKey(spki, {name: Ed25519}, true, [encrypt\])] @@ -1188,3 +1326,141 @@ [Invalid key pair: importKey(jwk(private), {name: Ed25519}, true, [sign, sign\])] expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, true, [verify, verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed25519}, false, [verify, verify, deriveBits\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed25519}, true, [verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed25519}, false, [verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed25519}, true, [verify, verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed25519}, false, [verify, verify\])] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js.ini b/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js.ini index e222def8988..9b794a6f26d 100644 --- a/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js.ini +++ b/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js.ini @@ -599,6 +599,144 @@ [Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign, sign\])] expected: FAIL + [Bad usages: importKey(raw, {name: Ed448}, true, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, deriveBits\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed448}, true, [verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed448}, false, [verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed448}, true, [verify, verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed448}, false, [verify, verify\])] + expected: FAIL + [okp_importKey_failures_Ed448.https.any.html] [Bad usages: importKey(spki, {name: Ed448}, true, [encrypt\])] @@ -1200,3 +1338,141 @@ [Invalid key pair: importKey(jwk(private), {name: Ed448}, true, [sign, sign\])] expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, deriveBits\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed448}, true, [verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed448}, false, [verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed448}, true, [verify, verify\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: Ed448}, false, [verify, verify\])] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js.ini b/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js.ini index c88fd422cbc..88d88b1f4aa 100644 --- a/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js.ini +++ b/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js.ini @@ -581,6 +581,60 @@ [Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits\])] expected: FAIL + [Bad usages: importKey(raw, {name: X25519}, true, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [verify\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [verify\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [deriveBits\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: X25519}, true, [\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: X25519}, false, [\])] + expected: FAIL + [okp_importKey_failures_X25519.https.any.html] [Bad usages: importKey(spki, {name: X25519}, true, [encrypt\])] @@ -1164,3 +1218,57 @@ [Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits\])] expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [verify\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [verify\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, true, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X25519}, false, [deriveBits\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: X25519}, true, [\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: X25519}, false, [\])] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js.ini b/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js.ini index b76b57f3270..2ea911c02b9 100644 --- a/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js.ini +++ b/tests/wpt/metadata-layout-2020/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js.ini @@ -593,6 +593,60 @@ [Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits\])] expected: FAIL + [Bad usages: importKey(raw, {name: X448}, true, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [verify\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [verify\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [deriveBits\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: X448}, true, [\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: X448}, false, [\])] + expected: FAIL + [okp_importKey_failures_X448.https.any.worker.html] [Bad usages: importKey(spki, {name: X448}, true, [encrypt\])] @@ -1188,3 +1242,57 @@ [Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits\])] expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [encrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [decrypt\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [sign\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [verify\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [verify\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [wrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [unwrapKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [deriveKey\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, true, [deriveBits\])] + expected: FAIL + + [Bad usages: importKey(raw, {name: X448}, false, [deriveBits\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: X448}, true, [\])] + expected: FAIL + + [Bad key length: importKey(raw, {name: X448}, false, [\])] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-003.html.ini b/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-003.html.ini new file mode 100644 index 00000000000..665ec4646cb --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-003.html.ini @@ -0,0 +1,3 @@ +[hit-test-floats-003.html] + [Miss float below something else] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/normal-flow/block-in-inline-hittest-relpos-zindex.html.ini b/tests/wpt/metadata-layout-2020/css/CSS2/normal-flow/block-in-inline-hittest-relpos-zindex.html.ini index cdd8b55f192..f9e216a8cfc 100644 --- a/tests/wpt/metadata-layout-2020/css/CSS2/normal-flow/block-in-inline-hittest-relpos-zindex.html.ini +++ b/tests/wpt/metadata-layout-2020/css/CSS2/normal-flow/block-in-inline-hittest-relpos-zindex.html.ini @@ -1,3 +1,6 @@ [block-in-inline-hittest-relpos-zindex.html] [position: relative; z-index: -1;] expected: FAIL + + [block-in-inline-hittest-relpos-zindex] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-fonts/variations/at-font-face-font-matching.html.ini b/tests/wpt/metadata-layout-2020/css/css-fonts/variations/at-font-face-font-matching.html.ini index 5f1ad263eb7..db1abfe9c1c 100644 --- a/tests/wpt/metadata-layout-2020/css/css-fonts/variations/at-font-face-font-matching.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-fonts/variations/at-font-face-font-matching.html.ini @@ -71,9 +71,6 @@ [Matching font-style: 'normal' should prefer 'oblique 0deg' over 'oblique 10deg 40deg'] expected: FAIL - [Matching font-style: 'normal' should prefer 'oblique 10deg 40deg' over 'oblique 20deg 30deg'] - expected: FAIL - [Matching font-style: 'italic' should prefer 'oblique 20deg' over 'oblique 30deg 60deg'] expected: FAIL @@ -149,9 +146,6 @@ [Matching font-weight: '501' should prefer '502 510' over '503 520'] expected: FAIL - [Matching font-style: 'normal' should prefer 'normal' over 'oblique 0deg'] - expected: FAIL - [Matching font-style: 'oblique 10deg' should prefer 'oblique 15deg 20deg' over 'oblique 30deg 60deg'] expected: FAIL @@ -314,5 +308,11 @@ [Matching font-style: 'italic' should prefer 'oblique 0deg' over 'oblique -60deg -30deg'] expected: FAIL - [Matching font-style: 'oblique 20deg' should prefer 'italic' over 'oblique 0deg'] + [Matching font-style: 'oblique 21deg' should prefer 'italic' over 'oblique 0deg'] + expected: FAIL + + [Matching font-style: 'oblique -10deg' should prefer 'oblique -50deg -40deg' over 'italic'] + expected: FAIL + + [Matching font-style: 'oblique -20deg' should prefer 'oblique -60deg -40deg' over 'oblique -10deg'] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-text-decor/animations/discrete-no-interpolation.html.ini b/tests/wpt/metadata-layout-2020/css/css-text-decor/animations/discrete-no-interpolation.html.ini new file mode 100644 index 00000000000..72cf9136a14 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-text-decor/animations/discrete-no-interpolation.html.ini @@ -0,0 +1,450 @@ +[discrete-no-interpolation.html] + [CSS Transitions with transition: all: property from [initial\] to [underline\] at (-0.3) should be [underline\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [underline\] at (0) should be [underline\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [underline\] at (0.3) should be [underline\]] + expected: FAIL + + [Web Animations: property from [initial\] to [underline\] at (-0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [underline\] at (0) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [underline\] at (0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [underline\] at (0.5) should be [underline\]] + expected: FAIL + + [Web Animations: property from [initial\] to [underline\] at (0.6) should be [underline\]] + expected: FAIL + + [Web Animations: property from [initial\] to [underline\] at (1) should be [underline\]] + expected: FAIL + + [Web Animations: property from [initial\] to [underline\] at (1.5) should be [underline\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dashed\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dashed\] at (0) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dashed\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dashed\] at (0.5) should be [dashed\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dashed\] at (0.6) should be [dashed\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dashed\] at (1) should be [dashed\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dashed\] at (1.5) should be [dashed\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dashed\] at (-0.3) should be [dashed\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dashed\] at (0) should be [dashed\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dashed\] at (0.3) should be [dashed\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dashed\] at (0.5) should be [dashed\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dashed\] at (0.6) should be [dashed\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dashed\] at (1) should be [dashed\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dashed\] at (1.5) should be [dashed\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dashed\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dashed\] at (0) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dashed\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dashed\] at (0.5) should be [dashed\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dashed\] at (0.6) should be [dashed\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dashed\] at (1) should be [dashed\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dashed\] at (1.5) should be [dashed\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dashed\] at (-0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dashed\] at (0) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dashed\] at (0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dashed\] at (0.5) should be [dashed\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dashed\] at (0.6) should be [dashed\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dashed\] at (1) should be [dashed\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dashed\] at (1.5) should be [dashed\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [123px\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [123px\] at (0) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [123px\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [123px\] at (0.5) should be [123px\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [123px\] at (0.6) should be [123px\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [123px\] at (1) should be [123px\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [123px\] at (1.5) should be [123px\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [123px\] at (-0.3) should be [123px\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [123px\] at (0) should be [123px\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [123px\] at (0.3) should be [123px\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [123px\] at (0.5) should be [123px\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [123px\] at (0.6) should be [123px\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [123px\] at (1) should be [123px\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [123px\] at (1.5) should be [123px\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [123px\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [123px\] at (0) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [123px\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [123px\] at (0.5) should be [123px\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [123px\] at (0.6) should be [123px\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [123px\] at (1) should be [123px\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [123px\] at (1.5) should be [123px\]] + expected: FAIL + + [Web Animations: property from [initial\] to [123px\] at (-0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [123px\] at (0) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [123px\] at (0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [123px\] at (0.5) should be [123px\]] + expected: FAIL + + [Web Animations: property from [initial\] to [123px\] at (0.6) should be [123px\]] + expected: FAIL + + [Web Animations: property from [initial\] to [123px\] at (1) should be [123px\]] + expected: FAIL + + [Web Animations: property from [initial\] to [123px\] at (1.5) should be [123px\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under right\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under right\] at (0) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under right\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under right\] at (0.5) should be [under right\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under right\] at (0.6) should be [under right\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under right\] at (1) should be [under right\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under right\] at (1.5) should be [under right\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under right\] at (-0.3) should be [under right\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under right\] at (0) should be [under right\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under right\] at (0.3) should be [under right\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under right\] at (0.5) should be [under right\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under right\] at (0.6) should be [under right\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under right\] at (1) should be [under right\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under right\] at (1.5) should be [under right\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under right\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under right\] at (0) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under right\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under right\] at (0.5) should be [under right\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under right\] at (0.6) should be [under right\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under right\] at (1) should be [under right\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under right\] at (1.5) should be [under right\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under right\] at (-0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under right\] at (0) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under right\] at (0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under right\] at (0.5) should be [under right\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under right\] at (0.6) should be [under right\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under right\] at (1) should be [under right\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under right\] at (1.5) should be [under right\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dot\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dot\] at (0) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dot\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dot\] at (0.5) should be [dot\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dot\] at (0.6) should be [dot\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dot\] at (1) should be [dot\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [dot\] at (1.5) should be [dot\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dot\] at (-0.3) should be [dot\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dot\] at (0) should be [dot\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dot\] at (0.3) should be [dot\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dot\] at (0.5) should be [dot\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dot\] at (0.6) should be [dot\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dot\] at (1) should be [dot\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [dot\] at (1.5) should be [dot\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dot\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dot\] at (0) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dot\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dot\] at (0.5) should be [dot\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dot\] at (0.6) should be [dot\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dot\] at (1) should be [dot\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [dot\] at (1.5) should be [dot\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dot\] at (-0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dot\] at (0) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dot\] at (0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dot\] at (0.5) should be [dot\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dot\] at (0.6) should be [dot\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dot\] at (1) should be [dot\]] + expected: FAIL + + [Web Animations: property from [initial\] to [dot\] at (1.5) should be [dot\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under\] at (0) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under\] at (0.5) should be [under\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under\] at (0.6) should be [under\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under\] at (1) should be [under\]] + expected: FAIL + + [CSS Transitions: property from [initial\] to [under\] at (1.5) should be [under\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under\] at (-0.3) should be [under\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under\] at (0) should be [under\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under\] at (0.3) should be [under\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under\] at (0.5) should be [under\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under\] at (0.6) should be [under\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under\] at (1) should be [under\]] + expected: FAIL + + [CSS Transitions with transition: all: property from [initial\] to [under\] at (1.5) should be [under\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under\] at (-0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under\] at (0) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under\] at (0.3) should be [initial\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under\] at (0.5) should be [under\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under\] at (0.6) should be [under\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under\] at (1) should be [under\]] + expected: FAIL + + [CSS Animations: property from [initial\] to [under\] at (1.5) should be [under\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under\] at (-0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under\] at (0) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under\] at (0.3) should be [initial\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under\] at (0.5) should be [under\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under\] at (0.6) should be [under\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under\] at (1) should be [under\]] + expected: FAIL + + [Web Animations: property from [initial\] to [under\] at (1.5) should be [under\]] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transitions/inert-while-transitioning-to-display-none.html.ini b/tests/wpt/metadata-layout-2020/css/css-transitions/inert-while-transitioning-to-display-none.html.ini new file mode 100644 index 00000000000..b8b24457c07 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-transitions/inert-while-transitioning-to-display-none.html.ini @@ -0,0 +1,3 @@ +[inert-while-transitioning-to-display-none.html] + [Elements which are transitioning to display:none should be inert.] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-001.html.ini b/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-001.html.ini index 7727764a576..4a3af6a97b1 100644 --- a/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-001.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-001.html.ini @@ -352,3 +352,126 @@ [margin-right length(mm) / values] expected: FAIL + + [background-color color(rgba) / values] + expected: FAIL + + [border-top-width length(pt) / values] + expected: FAIL + + [border-top-width length(pc) / values] + expected: FAIL + + [border-top-width length(px) / values] + expected: FAIL + + [border-top-width length(em) / values] + expected: FAIL + + [border-top-width length(ex) / values] + expected: FAIL + + [border-top-width length(mm) / values] + expected: FAIL + + [border-top-width length(cm) / values] + expected: FAIL + + [border-top-width length(in) / values] + expected: FAIL + + [border-right-width length(pt) / values] + expected: FAIL + + [border-right-width length(pc) / values] + expected: FAIL + + [border-right-width length(px) / values] + expected: FAIL + + [border-right-width length(em) / values] + expected: FAIL + + [border-right-width length(ex) / values] + expected: FAIL + + [border-right-width length(mm) / values] + expected: FAIL + + [border-right-width length(cm) / values] + expected: FAIL + + [border-right-width length(in) / values] + expected: FAIL + + [border-bottom-width length(pt) / values] + expected: FAIL + + [border-bottom-width length(pc) / values] + expected: FAIL + + [border-bottom-width length(px) / values] + expected: FAIL + + [border-bottom-width length(em) / values] + expected: FAIL + + [border-bottom-width length(ex) / values] + expected: FAIL + + [border-bottom-width length(mm) / values] + expected: FAIL + + [border-bottom-width length(cm) / values] + expected: FAIL + + [border-bottom-width length(in) / values] + expected: FAIL + + [border-left-width length(pt) / values] + expected: FAIL + + [border-left-width length(pc) / values] + expected: FAIL + + [border-left-width length(px) / values] + expected: FAIL + + [border-left-width length(em) / values] + expected: FAIL + + [border-left-width length(ex) / values] + expected: FAIL + + [border-left-width length(mm) / values] + expected: FAIL + + [border-left-width length(cm) / values] + expected: FAIL + + [border-left-width length(in) / values] + expected: FAIL + + [border-top-color color(rgba) / values] + expected: FAIL + + [border-right-color color(rgba) / values] + expected: FAIL + + [border-bottom-color color(rgba) / values] + expected: FAIL + + [border-left-color color(rgba) / values] + expected: FAIL + + [padding-bottom length(pt) / values] + expected: FAIL + + [padding-bottom length(pc) / values] + expected: FAIL + + [padding-bottom length(px) / values] + expected: FAIL + + [padding-bottom length(em) / values] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-inherit-001.html.ini b/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-inherit-001.html.ini index 5dc6354ad55..b0fdc6ab5f2 100644 --- a/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-inherit-001.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-inherit-001.html.ini @@ -628,3 +628,153 @@ [text-shadow shadow(shadow) / events] expected: FAIL + + [max-height length(ex) / events] + expected: FAIL + + [max-height length(mm) / events] + expected: FAIL + + [max-height length(cm) / events] + expected: FAIL + + [max-height length(in) / events] + expected: FAIL + + [max-height percentage(%) / events] + expected: FAIL + + [max-width length(pt) / events] + expected: FAIL + + [max-width length(pc) / events] + expected: FAIL + + [max-width length(px) / events] + expected: FAIL + + [max-width length(em) / events] + expected: FAIL + + [max-width length(ex) / events] + expected: FAIL + + [max-width length(mm) / events] + expected: FAIL + + [max-width length(cm) / events] + expected: FAIL + + [max-width length(in) / events] + expected: FAIL + + [max-width percentage(%) / events] + expected: FAIL + + [top length(pt) / events] + expected: FAIL + + [top length(pc) / events] + expected: FAIL + + [top length(px) / events] + expected: FAIL + + [top length(em) / events] + expected: FAIL + + [top length(ex) / events] + expected: FAIL + + [top length(mm) / events] + expected: FAIL + + [top length(cm) / events] + expected: FAIL + + [top length(in) / events] + expected: FAIL + + [top percentage(%) / events] + expected: FAIL + + [right length(pt) / events] + expected: FAIL + + [right length(pc) / events] + expected: FAIL + + [right length(px) / events] + expected: FAIL + + [right length(em) / events] + expected: FAIL + + [right length(ex) / events] + expected: FAIL + + [right length(mm) / events] + expected: FAIL + + [right length(cm) / events] + expected: FAIL + + [right length(in) / events] + expected: FAIL + + [right percentage(%) / events] + expected: FAIL + + [bottom length(pt) / events] + expected: FAIL + + [bottom length(pc) / events] + expected: FAIL + + [bottom length(px) / events] + expected: FAIL + + [bottom length(em) / events] + expected: FAIL + + [bottom length(ex) / events] + expected: FAIL + + [bottom length(mm) / events] + expected: FAIL + + [bottom length(cm) / events] + expected: FAIL + + [bottom length(in) / events] + expected: FAIL + + [bottom percentage(%) / events] + expected: FAIL + + [left length(pt) / events] + expected: FAIL + + [left length(pc) / events] + expected: FAIL + + [left length(px) / events] + expected: FAIL + + [left length(em) / events] + expected: FAIL + + [left length(ex) / events] + expected: FAIL + + [left length(mm) / events] + expected: FAIL + + [left length(cm) / events] + expected: FAIL + + [left length(in) / events] + expected: FAIL + + [left percentage(%) / events] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-inherit-002.html.ini b/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-inherit-002.html.ini index 793afafa0e8..242ba44bbca 100644 --- a/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-inherit-002.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-transitions/properties-value-inherit-002.html.ini @@ -52,276 +52,3 @@ [background-position length(px) / events] expected: FAIL - - [background-color color(rgba) / values] - expected: FAIL - - [border-top-width length(pt) / values] - expected: FAIL - - [border-top-width length(pc) / values] - expected: FAIL - - [border-top-width length(px) / values] - expected: FAIL - - [border-top-width length(em) / values] - expected: FAIL - - [border-top-width length(ex) / values] - expected: FAIL - - [border-top-width length(mm) / values] - expected: FAIL - - [border-top-width length(cm) / values] - expected: FAIL - - [border-top-width length(in) / values] - expected: FAIL - - [border-right-width length(pt) / values] - expected: FAIL - - [border-right-width length(pc) / values] - expected: FAIL - - [border-right-width length(px) / values] - expected: FAIL - - [border-right-width length(em) / values] - expected: FAIL - - [border-right-width length(ex) / values] - expected: FAIL - - [border-right-width length(mm) / values] - expected: FAIL - - [border-right-width length(cm) / values] - expected: FAIL - - [border-right-width length(in) / values] - expected: FAIL - - [border-bottom-width length(pt) / values] - expected: FAIL - - [border-bottom-width length(pc) / values] - expected: FAIL - - [border-bottom-width length(px) / values] - expected: FAIL - - [border-bottom-width length(em) / values] - expected: FAIL - - [border-bottom-width length(ex) / values] - expected: FAIL - - [border-bottom-width length(mm) / values] - expected: FAIL - - [border-bottom-width length(cm) / values] - expected: FAIL - - [border-bottom-width length(in) / values] - expected: FAIL - - [border-left-width length(pt) / values] - expected: FAIL - - [border-left-width length(pc) / values] - expected: FAIL - - [border-left-width length(px) / values] - expected: FAIL - - [border-left-width length(em) / values] - expected: FAIL - - [border-left-width length(ex) / values] - expected: FAIL - - [border-left-width length(mm) / values] - expected: FAIL - - [border-left-width length(cm) / values] - expected: FAIL - - [border-left-width length(in) / values] - expected: FAIL - - [border-top-color color(rgba) / values] - expected: FAIL - - [border-right-color color(rgba) / values] - expected: FAIL - - [border-bottom-color color(rgba) / values] - expected: FAIL - - [border-left-color color(rgba) / values] - expected: FAIL - - [padding-bottom length(pt) / values] - expected: FAIL - - [padding-bottom length(pc) / values] - expected: FAIL - - [padding-bottom length(px) / values] - expected: FAIL - - [padding-bottom length(em) / values] - expected: FAIL - - [color color(rgba) / values] - expected: FAIL - - [font-size length(pt) / values] - expected: FAIL - - [font-size length(pc) / values] - expected: FAIL - - [font-size length(px) / values] - expected: FAIL - - [font-size length(em) / values] - expected: FAIL - - [font-size length(ex) / values] - expected: FAIL - - [font-size length(mm) / values] - expected: FAIL - - [font-size length(cm) / values] - expected: FAIL - - [font-size length(in) / values] - expected: FAIL - - [font-size percentage(%) / values] - expected: FAIL - - [font-weight font-weight(keyword) / values] - expected: FAIL - - [font-weight font-weight(numeric) / values] - expected: FAIL - - [line-height number(integer) / values] - expected: FAIL - - [line-height number(decimal) / values] - expected: FAIL - - [line-height length(pt) / values] - expected: FAIL - - [line-height length(pc) / values] - expected: FAIL - - [line-height length(px) / values] - expected: FAIL - - [line-height length(em) / values] - expected: FAIL - - [line-height length(ex) / values] - expected: FAIL - - [line-height length(mm) / values] - expected: FAIL - - [line-height length(cm) / values] - expected: FAIL - - [line-height length(in) / values] - expected: FAIL - - [line-height percentage(%) / values] - expected: FAIL - - [letter-spacing length(pt) / values] - expected: FAIL - - [letter-spacing length(pc) / values] - expected: FAIL - - [letter-spacing length(px) / values] - expected: FAIL - - [letter-spacing length(em) / values] - expected: FAIL - - [letter-spacing length(ex) / values] - expected: FAIL - - [letter-spacing length(mm) / values] - expected: FAIL - - [letter-spacing length(cm) / values] - expected: FAIL - - [letter-spacing length(in) / values] - expected: FAIL - - [word-spacing length(pt) / values] - expected: FAIL - - [word-spacing length(pc) / values] - expected: FAIL - - [word-spacing length(px) / values] - expected: FAIL - - [word-spacing length(em) / values] - expected: FAIL - - [word-spacing length(ex) / values] - expected: FAIL - - [word-spacing length(mm) / values] - expected: FAIL - - [word-spacing length(cm) / values] - expected: FAIL - - [word-spacing length(in) / values] - expected: FAIL - - [word-spacing percentage(%) / values] - expected: FAIL - - [text-indent length(pt) / values] - expected: FAIL - - [text-indent length(pc) / values] - expected: FAIL - - [text-indent length(px) / values] - expected: FAIL - - [text-indent length(em) / values] - expected: FAIL - - [text-indent length(ex) / values] - expected: FAIL - - [text-indent length(mm) / values] - expected: FAIL - - [text-indent length(cm) / values] - expected: FAIL - - [text-indent length(in) / values] - expected: FAIL - - [text-indent percentage(%) / values] - expected: FAIL - - [text-shadow shadow(shadow) / values] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-computed.html.ini b/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-computed.html.ini index 3920bbbf97d..f1c17a32e8c 100644 --- a/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-computed.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-computed.html.ini @@ -115,3 +115,30 @@ [Property width value 'min(NaN * 1px, 15px)'] expected: FAIL + + [Property rotate(calc(infinity * 1turn)) value expected same with rotate(0deg) in +/-0.0001] + expected: FAIL + + [Property rotate(calc(-infinity * 1turn)) value expected same with rotate(0deg) in +/-0.0001] + expected: FAIL + + [Property rotate(calc(NaN * 1turn)) value expected same with rotate(0deg) in +/-0.0001] + expected: FAIL + + [Property rotate(calc(infinity * 1rad)) value expected same with rotate(0deg) in +/-0.0001] + expected: FAIL + + [Property rotate(calc(-infinity * 1rad)) value expected same with rotate(0deg) in +/-0.0001] + expected: FAIL + + [Property rotate(calc(NaN * 1rad)) value expected same with rotate(0deg) in +/-0.0001] + expected: FAIL + + [Property rotate(calc(infinity * 1grad)) value expected same with rotate(0deg) in +/-0.0001] + expected: FAIL + + [Property rotate(calc(-infinity * 1grad)) value expected same with rotate(0deg) in +/-0.0001] + expected: FAIL + + [Property rotate(calc(NaN * 1grad)) value expected same with rotate(0deg) in +/-0.0001] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-serialize-length.html.ini b/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-serialize-length.html.ini index 6946cb88254..6d1f2802624 100644 --- a/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-serialize-length.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-serialize-length.html.ini @@ -82,3 +82,21 @@ ['calc(1px * clamp(-inFinity, infinity, 10))' as a specified value should serialize as 'calc(10px)'.] expected: FAIL + + ['calc(1in * NaN)' as a specified value should serialize as 'calc(NaN * 1px)'.] + expected: FAIL + + ['calc(1cm * NaN)' as a specified value should serialize as 'calc(NaN * 1px)'.] + expected: FAIL + + ['calc(1mm * NaN)' as a specified value should serialize as 'calc(NaN * 1px)'.] + expected: FAIL + + ['calc(1q * NaN)' as a specified value should serialize as 'calc(NaN * 1px)'.] + expected: FAIL + + ['calc(1pt * NaN)' as a specified value should serialize as 'calc(NaN * 1px)'.] + expected: FAIL + + ['calc(1pc * NaN)' as a specified value should serialize as 'calc(NaN * 1px)'.] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-serialize-resolution.html.ini b/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-serialize-resolution.html.ini new file mode 100644 index 00000000000..e58a0d25026 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-values/calc-infinity-nan-serialize-resolution.html.ini @@ -0,0 +1,87 @@ +[calc-infinity-nan-serialize-resolution.html] + ['image-set(url("") calc(1x * NaN))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1x * nan))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * NaN))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * infinity / infinity))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * 0 * infinity))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * (infinity + -infinity)))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * (-infinity + infinity)))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * (infinity - infinity)))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * infinity))' as a specified value should serialize as 'image-set(url("") calc(infinity * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * -infinity))' as a specified value should serialize as 'image-set(url("") calc(-infinity * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * iNFinIty))' as a specified value should serialize as 'image-set(url("") calc(infinity * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * (infinity + infinity)))' as a specified value should serialize as 'image-set(url("") calc(infinity * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * (-infinity + -infinity)))' as a specified value should serialize as 'image-set(url("") calc(-infinity * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * 1/infinity))' as a specified value should serialize as 'image-set(url("") calc(0dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * infinity * infinity))' as a specified value should serialize as 'image-set(url("") calc(infinity * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * -infinity * -infinity))' as a specified value should serialize as 'image-set(url("") calc(infinity * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1 * max(INFinity*3dppx, 0dppx)))' as a specified value should serialize as 'image-set(url("") calc(infinity * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1 * min(inFInity*4dppx, 0dppx)))' as a specified value should serialize as 'image-set(url("") calc(0dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1 * max(nAn*2dppx, 0dppx)))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1 * min(nan*3dppx, 0dppx)))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1 * clamp(-INFINITY*0dppx, 0dppx, infiniTY*0dppx)))' as a specified value should serialize as 'image-set(url("") calc(0dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * max(NaN, min(0,10))))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * clamp(NaN, 0, 10)))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * max(0, min(10, NaN))))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * clamp(0, 10, NaN)))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * max(0, min(NaN, 10))))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * clamp(0, NaN, 10)))' as a specified value should serialize as 'image-set(url("") calc(NaN * 1dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * clamp(-Infinity, 0, infinity)))' as a specified value should serialize as 'image-set(url("") calc(0dppx))'.] + expected: FAIL + + ['image-set(url("") calc(1dppx * clamp(-inFinity, infinity, 10)))' as a specified value should serialize as 'image-set(url("") calc(10dppx))'.] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-values/getComputedStyle-calc-bg-height-percent-001.html.ini b/tests/wpt/metadata-layout-2020/css/css-values/getComputedStyle-calc-bg-height-percent-001.html.ini new file mode 100644 index 00000000000..558d325210d --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-values/getComputedStyle-calc-bg-height-percent-001.html.ini @@ -0,0 +1,3 @@ +[getComputedStyle-calc-bg-height-percent-001.html] + [testing background-position: calc(100% - 100% + 20em)] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-cssText.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-cssText.html.ini index 9682bdd9d65..a360bf99f1b 100644 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-cssText.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-variables/variable-cssText.html.ini @@ -10,3 +10,6 @@ [target9] expected: FAIL + + [target11] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-dynamic-anon-box.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-dynamic-anon-box.html.ini new file mode 100644 index 00000000000..d1b2e9e0b54 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-dynamic-anon-box.html.ini @@ -0,0 +1,2 @@ +[elementFromPoint-dynamic-anon-box.html] + expected: CRASH diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-ellipsis-in-inline-box.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-ellipsis-in-inline-box.html.ini deleted file mode 100644 index b905fe70fcb..00000000000 --- a/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-ellipsis-in-inline-box.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[elementFromPoint-ellipsis-in-inline-box.html] - [elementFromPoint-ellipsis-in-inline-box] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-float-in-table.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-float-in-table.html.ini new file mode 100644 index 00000000000..db065588308 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-float-in-table.html.ini @@ -0,0 +1,3 @@ +[elementFromPoint-float-in-table.html] + [float-in-div] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-subpixel.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-subpixel.html.ini new file mode 100644 index 00000000000..8f6f663ba61 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-subpixel.html.ini @@ -0,0 +1,3 @@ +[elementFromPoint-subpixel.html] + [Hit test top left corner of box] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-visibility-hidden-resizer.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-visibility-hidden-resizer.html.ini deleted file mode 100644 index a6bca7ac372..00000000000 --- a/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPoint-visibility-hidden-resizer.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[elementFromPoint-visibility-hidden-resizer.html] - [elementFromPoint on resizer area of an element with visibility:hidden] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPosition.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPosition.html.ini index 1bf6ea475bf..8e5101c386c 100644 --- a/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPosition.html.ini +++ b/tests/wpt/metadata-layout-2020/css/cssom-view/elementFromPosition.html.ini @@ -13,6 +13,3 @@ [test some point of the element: bottom right corner] expected: FAIL - - [test the top of layer] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/elementsFromPoint-invalid-cases.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/elementsFromPoint-invalid-cases.html.ini new file mode 100644 index 00000000000..a3496a40d07 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/cssom-view/elementsFromPoint-invalid-cases.html.ini @@ -0,0 +1,3 @@ +[elementsFromPoint-invalid-cases.html] + [The root element is the last element returned for otherwise empty queries within the viewport] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/mediaqueries/prefers-reduced-transparency.html.ini b/tests/wpt/metadata-layout-2020/css/mediaqueries/prefers-reduced-transparency.html.ini new file mode 100644 index 00000000000..7132494be3e --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/mediaqueries/prefers-reduced-transparency.html.ini @@ -0,0 +1,30 @@ +[prefers-reduced-transparency.html] + [Should be known: '(prefers-reduced-transparency)'] + expected: FAIL + + [Should be known: '(prefers-reduced-transparency: no-preference)'] + expected: FAIL + + [Should be known: '(prefers-reduced-transparency: reduce)'] + expected: FAIL + + [Should be parseable: '(prefers-reduced-transparency: 0)'] + expected: FAIL + + [Should be parseable: '(prefers-reduced-transparency: none)'] + expected: FAIL + + [Should be parseable: '(prefers-reduced-transparency: 10px)'] + expected: FAIL + + [Should be parseable: '(prefers-reduced-transparency: no-preference reduce)'] + expected: FAIL + + [Should be parseable: '(prefers-reduced-transparency: reduced)'] + expected: FAIL + + [Should be parseable: '(prefers-reduced-transparency: no-preference/reduce)'] + expected: FAIL + + [Check that no-preference evaluates to false in the boolean context] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-class-prefix.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-class-prefix.html.ini new file mode 100644 index 00000000000..effa60039b6 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-class-prefix.html.ini @@ -0,0 +1,2 @@ +[nth-child-of-class-prefix.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-id-prefix.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-id-prefix.html.ini new file mode 100644 index 00000000000..1612e605758 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-id-prefix.html.ini @@ -0,0 +1,2 @@ +[nth-child-of-id-prefix.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-ids.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-ids.html.ini new file mode 100644 index 00000000000..114e02e45fb --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-ids.html.ini @@ -0,0 +1,2 @@ +[nth-child-of-ids.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-in-is.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-in-is.html.ini new file mode 100644 index 00000000000..d2d0e9deab3 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-in-is.html.ini @@ -0,0 +1,2 @@ +[nth-child-of-in-is.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-in-shadow-root.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-in-shadow-root.html.ini new file mode 100644 index 00000000000..a6bff34a70e --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-in-shadow-root.html.ini @@ -0,0 +1,2 @@ +[nth-child-of-in-shadow-root.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-is.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-is.html.ini new file mode 100644 index 00000000000..4ad8c185397 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-is.html.ini @@ -0,0 +1,2 @@ +[nth-child-of-is.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-pseudo-class.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-pseudo-class.html.ini new file mode 100644 index 00000000000..a3c4f6a22df --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-child-of-pseudo-class.html.ini @@ -0,0 +1,2 @@ +[nth-child-of-pseudo-class.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-class-prefix.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-class-prefix.html.ini new file mode 100644 index 00000000000..c1153dedf0d --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-class-prefix.html.ini @@ -0,0 +1,2 @@ +[nth-last-child-of-class-prefix.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-id-prefix.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-id-prefix.html.ini new file mode 100644 index 00000000000..8f1d8b3e2f0 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-id-prefix.html.ini @@ -0,0 +1,2 @@ +[nth-last-child-of-id-prefix.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-ids.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-ids.html.ini new file mode 100644 index 00000000000..126bbeb14de --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-ids.html.ini @@ -0,0 +1,2 @@ +[nth-last-child-of-ids.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-in-is.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-in-is.html.ini new file mode 100644 index 00000000000..4a29e58633c --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-in-is.html.ini @@ -0,0 +1,2 @@ +[nth-last-child-of-in-is.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-in-shadow-root.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-in-shadow-root.html.ini new file mode 100644 index 00000000000..2f69cd5da3d --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-in-shadow-root.html.ini @@ -0,0 +1,2 @@ +[nth-last-child-of-in-shadow-root.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-is.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-is.html.ini new file mode 100644 index 00000000000..80846d39331 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-is.html.ini @@ -0,0 +1,2 @@ +[nth-last-child-of-is.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-pseudo-class.html.ini b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-pseudo-class.html.ini new file mode 100644 index 00000000000..d246868243f --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/selectors/invalidation/nth-last-child-of-pseudo-class.html.ini @@ -0,0 +1,2 @@ +[nth-last-child-of-pseudo-class.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/custom-elements/form-associated/disabled-delegatesFocus.html.ini b/tests/wpt/metadata-layout-2020/custom-elements/form-associated/disabled-delegatesFocus.html.ini new file mode 100644 index 00000000000..46943b32055 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/custom-elements/form-associated/disabled-delegatesFocus.html.ini @@ -0,0 +1,2 @@ +[disabled-delegatesFocus.html] + expected: ERROR diff --git a/tests/wpt/metadata-layout-2020/fetch/metadata/generated/css-images.sub.tentative.html.ini b/tests/wpt/metadata-layout-2020/fetch/metadata/generated/css-images.sub.tentative.html.ini index f9de5391ad6..741af78eff8 100644 --- a/tests/wpt/metadata-layout-2020/fetch/metadata/generated/css-images.sub.tentative.html.ini +++ b/tests/wpt/metadata-layout-2020/fetch/metadata/generated/css-images.sub.tentative.html.ini @@ -146,3 +146,9 @@ [list-style-image sec-fetch-site - HTTPS downgrade-upgrade] expected: FAIL + + [background-image sec-fetch-site - HTTPS downgrade (header not sent)] + expected: TIMEOUT + + [border-image sec-fetch-site - HTTPS downgrade (header not sent)] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html.ini b/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html.ini index 1f528be38ef..a3f473a9557 100644 --- a/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html.ini +++ b/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html.ini @@ -1,4 +1,4 @@ [scroll-restoration-fragment-scrolling-cross-origin.html] - expected: ERROR + expected: TIMEOUT [Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation] expected: TIMEOUT diff --git a/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html.ini b/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html.ini index 1d68034a37d..ab9ac6e835e 100644 --- a/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html.ini +++ b/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html.ini @@ -1,12 +1,13 @@ [failure-check-sequence.https.html] + expected: TIMEOUT [CSP check precedes COEP check - CSP header first] - expected: FAIL + expected: NOTRUN [COEP check precedes X-Frame-Options check] - expected: FAIL + expected: NOTRUN [CSP check precedes COEP check - COEP header first] - expected: FAIL + expected: NOTRUN [CSP check precedes X-Frame-Options check] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html.ini b/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html.ini index 51fd557bd7f..b6aa0bab7b8 100644 --- a/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html.ini +++ b/tests/wpt/metadata-layout-2020/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html.ini @@ -7,3 +7,6 @@ [load & pageshow events do not fire on contentWindow of "> + diff --git a/tests/wpt/web-platform-tests/html/semantics/disabled-elements/event-propagate-disabled-keyboard.tentative.html b/tests/wpt/web-platform-tests/html/semantics/disabled-elements/event-propagate-disabled-keyboard.tentative.html new file mode 100644 index 00000000000..3aacf21f1d8 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/disabled-elements/event-propagate-disabled-keyboard.tentative.html @@ -0,0 +1,135 @@ + + +KeyboardEvent propagation on disabled form elements + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Span
+ + Text +
+ + diff --git a/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html b/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html index bf65172bde2..f6ae65708c6 100644 --- a/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html +++ b/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html @@ -39,7 +39,7 @@ onload = function() { var expected = {wide:resolve(img.dataset.wide), narrow:resolve(img.dataset.narrow)}; var current = iframe.className; var next = current === 'wide' ? 'narrow' : 'wide'; - var expect_change = expected[next].indexOf('undecodable.png') === -1 && !('noChange' in img.dataset); + var expect_change = expected[next].indexOf('broken.png') === -1 && !('noChange' in img.dataset); test(function() { assert_equals(img.currentSrc, expected[current]); diff --git a/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm b/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm index 7819ee1c189..f9426a529c1 100644 --- a/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm +++ b/tests/wpt/web-platform-tests/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm @@ -44,8 +44,6 @@ async_test(function(t) { v.removeAttribute("src"); v.load(); // Dimensions should be updated only on next layout. - assert_equals(s.width, '320px'); - assert_equals(s.height, '240px'); requestAnimationFrame(t.step_func_done(function() { assert_equals(s.width, "300px"); assert_equals(s.height, "150px"); @@ -62,8 +60,6 @@ async_test(function(t) { assert_equals(s.height, '77px'); v.removeAttribute("poster"); // Dimensions should be updated only on next layout. - assert_equals(s.width, '102px'); - assert_equals(s.height, '77px'); requestAnimationFrame(t.step_func_done(function() { assert_equals(s.width, "300px"); assert_equals(s.height, "150px"); diff --git a/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/placeholder-update-ref.html b/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/placeholder-update-ref.html new file mode 100644 index 00000000000..38da0195395 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/placeholder-update-ref.html @@ -0,0 +1,2 @@ + + diff --git a/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/placeholder-update.html b/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/placeholder-update.html new file mode 100644 index 00000000000..af0c0793ee1 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/placeholder-update.html @@ -0,0 +1,14 @@ + + + + +
+ + + diff --git a/tests/wpt/web-platform-tests/html/semantics/popovers/popover-attribute-all-elements.html b/tests/wpt/web-platform-tests/html/semantics/popovers/popover-attribute-all-elements.html index d620f767882..9ee659962b8 100644 --- a/tests/wpt/web-platform-tests/html/semantics/popovers/popover-attribute-all-elements.html +++ b/tests/wpt/web-platform-tests/html/semantics/popovers/popover-attribute-all-elements.html @@ -15,8 +15,8 @@ diff --git a/tests/wpt/web-platform-tests/html/semantics/popovers/popover-attribute-basic.html b/tests/wpt/web-platform-tests/html/semantics/popovers/popover-attribute-basic.html index c5937cd3237..56d746e9dbe 100644 --- a/tests/wpt/web-platform-tests/html/semantics/popovers/popover-attribute-basic.html +++ b/tests/wpt/web-platform-tests/html/semantics/popovers/popover-attribute-basic.html @@ -56,7 +56,7 @@ window.onload = () => { // Start with the provided examples: Array.from(document.getElementById('popovers').children).forEach(popover => { test((t) => { - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); }, `The element ${popover.outerHTML} should behave as a popover.`); }); Array.from(document.getElementById('nonpopovers').children).forEach(nonPopover => { @@ -121,34 +121,34 @@ window.onload = () => { test((t) => { const popover = createPopover(t); - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.removeAttribute('popover'); assertNotAPopover(popover); popover.setAttribute('popover','AuTo'); - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.removeAttribute('popover'); popover.setAttribute('PoPoVeR','AuTo'); - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); // Via IDL also popover.popover = 'auto'; - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.popover = 'aUtO'; - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.popover = 'invalid'; // treated as "manual" - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); },'Popover attribute value should be case insensitive'); test((t) => { const popover = createPopover(t); - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.setAttribute('popover','manual'); // Change popover type - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.setAttribute('popover','invalid'); // Change popover type to something invalid - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.popover = 'manual'; // Change popover type via IDL - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); popover.popover = 'invalid'; // Make invalid via IDL (treated as "manual") - assertIsFunctionalPopover(popover); + assertIsFunctionalPopover(popover, true); },'Changing attribute values for popover should work'); test((t) => { diff --git a/tests/wpt/web-platform-tests/html/semantics/popovers/popover-focus-2.html b/tests/wpt/web-platform-tests/html/semantics/popovers/popover-focus-2.html index 463a048436d..74ccc456ab6 100644 --- a/tests/wpt/web-platform-tests/html/semantics/popovers/popover-focus-2.html +++ b/tests/wpt/web-platform-tests/html/semantics/popovers/popover-focus-2.html @@ -106,16 +106,16 @@ promise_test(async t => { }, "Circular reference tab navigation"); -
- -
- +
+ +
+
+ +
+ +
Popover with
+ Other focusable element +
+ diff --git a/tests/wpt/web-platform-tests/html/semantics/popovers/popover-focus.html b/tests/wpt/web-platform-tests/html/semantics/popovers/popover-focus.html index 771efada1ad..aad9ab99dc2 100644 --- a/tests/wpt/web-platform-tests/html/semantics/popovers/popover-focus.html +++ b/tests/wpt/web-platform-tests/html/semantics/popovers/popover-focus.html @@ -196,7 +196,7 @@ assert_true(popover.matches(':open')); await clickOn(button); // This will *not* light dismiss, but will "toggle" the popover. assert_false(popover.matches(':open')); - assert_equals(document.activeElement, priorFocus, 'focus should return to the prior focus'); + assert_equals(document.activeElement, button, 'focus should move to the button when clicked, and should stay there when the popover closes'); assert_false(isElementVisible(popover)); // Same thing, but the button is contained within the popover diff --git a/tests/wpt/web-platform-tests/html/semantics/popovers/resources/popover-utils.js b/tests/wpt/web-platform-tests/html/semantics/popovers/resources/popover-utils.js index 7eb9e6327a2..b8b58178511 100644 --- a/tests/wpt/web-platform-tests/html/semantics/popovers/resources/popover-utils.js +++ b/tests/wpt/web-platform-tests/html/semantics/popovers/resources/popover-utils.js @@ -130,26 +130,26 @@ function assertPopoverVisibility(popover, isPopover, expectedVisibility, message } } -function assertIsFunctionalPopover(popover) { +function assertIsFunctionalPopover(popover, checkVisibility) { assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'A popover should start out hidden'); popover.showPopover(); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After showPopover(), a popover should be visible'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After showPopover(), a popover should be visible'); assert_throws_dom("InvalidStateError",() => popover.showPopover(),'Calling showPopover on a showing popover should throw InvalidStateError'); popover.hidePopover(); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After hidePopover(), a popover should be hidden'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After hidePopover(), a popover should be hidden'); assert_throws_dom("InvalidStateError",() => popover.hidePopover(),'Calling hidePopover on a hidden popover should throw InvalidStateError'); popover.togglePopover(); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover() on hidden popover, it should be visible'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover() on hidden popover, it should be visible'); popover.togglePopover(); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover() on visible popover, it should be hidden'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover() on visible popover, it should be hidden'); popover.togglePopover(/*force=*/true); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on hidden popover, it should be visible'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on hidden popover, it should be visible'); popover.togglePopover(/*force=*/true); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on visible popover, it should be visible'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on visible popover, it should be visible'); popover.togglePopover(/*force=*/false); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on visible popover, it should be hidden'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on visible popover, it should be hidden'); popover.togglePopover(/*force=*/false); - assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on hidden popover, it should be hidden'); + if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on hidden popover, it should be hidden'); const parent = popover.parentElement; popover.remove(); assert_throws_dom("InvalidStateError",() => popover.showPopover(),'Calling showPopover on a disconnected popover should throw InvalidStateError'); diff --git a/tests/wpt/web-platform-tests/lint.ignore b/tests/wpt/web-platform-tests/lint.ignore index 743986b66ee..6cc351e4948 100644 --- a/tests/wpt/web-platform-tests/lint.ignore +++ b/tests/wpt/web-platform-tests/lint.ignore @@ -215,6 +215,7 @@ SET TIMEOUT: resize-observer/resources/iframe.html SET TIMEOUT: resource-timing/resources/nested-contexts.js SET TIMEOUT: reporting/resources/first-csp-report.https.sub.html SET TIMEOUT: reporting/resources/second-csp-report.https.sub.html +SET TIMEOUT: scheduler/tentative/yield/yield-priority-timers.any.js SET TIMEOUT: secure-contexts/basic-popup-and-iframe-tests.https.js SET TIMEOUT: service-workers/cache-storage/cache-abort.https.any.js SET TIMEOUT: service-workers/service-worker/activation.https.html @@ -836,6 +837,7 @@ MISSING-TESTHARNESSREPORT: accessibility/crashtests/computed-node-checked.html PRINT STATEMENT: webdriver/tests/print/* PRINT STATEMENT: webdriver/tests/bidi/browsing_context/print/* +PRINT STATEMENT: webdriver/tests/support/fixtures_bidi.py DUPLICATE-BASENAME-PATH: acid/acid3/empty.html DUPLICATE-BASENAME-PATH: acid/acid3/empty.xml diff --git a/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-buffered.html b/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-buffered.html index 9956aeda93d..1a07036b157 100644 --- a/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-buffered.html +++ b/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-buffered.html @@ -20,7 +20,7 @@ promise_test(async t => { resolve("entry-found"); } })).observe({type: 'long-animation-frame', buffered: true}); - t.step_timeout(() => resolve("timeout"), no_long_frame_timeout); + t.step_timeout(() => resolve("timeout"), waiting_for_long_frame_timeout); }); assert_equals(result, "entry-found"); }, 'PerformanceObserver with buffered flag can see previous long-animation-frame entries.'); diff --git a/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-desired-exec-time.html b/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-desired-exec-time.html index 5fecb20fb40..d47ca28c303 100644 --- a/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-desired-exec-time.html +++ b/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-desired-exec-time.html @@ -93,9 +93,9 @@ promise_test(async t => { const scripts = entry.scripts.filter( s => s.name === "Window.requestAnimationFrame"); for (const script of scripts) { - assert_less_than_equal(Math.abs(script.desiredExecutionStart - rafTime), 1); + assert_approx_equals(script.desiredExecutionStart, rafTime, 5); } - assert_less_than_equal(Math.abs(entry.desiredRenderStart - rafTime), 1); + assert_approx_equals(entry.desiredRenderStart, rafTime, 5); }, "desiredExecutionStart & desiredRenderStart for requestAnimationFrame " + "should be the same as the rAF argument"); @@ -113,7 +113,7 @@ promise_test(async t => { timeAfterWait = performance.now(); }), 0); const [entry, rafTime] = await Promise.all([entryPromise, rafPromise]); - assert_less_than_equal(Math.abs(entry.desiredRenderStart - rafTime), 1); + assert_approx_equals(entry.desiredRenderStart, rafTime, 5); }, "desiredRenderStart and renderStart should reflect main thread delays"); diff --git a/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-first-ui-event.html b/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-first-ui-event.html new file mode 100644 index 00000000000..f2d8dac3f11 --- /dev/null +++ b/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-first-ui-event.html @@ -0,0 +1,101 @@ + + +Long Animation Frame Timing: first UI Event + + + + + + + + + +

Long Animation Frame: First UI Event

+
+ + diff --git a/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-promise.html b/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-promise.html index 226d06a8303..5ead569c8ae 100644 --- a/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-promise.html +++ b/tests/wpt/web-platform-tests/long-animation-frame/tentative/loaf-promise.html @@ -1,6 +1,6 @@ -Long Animation Frame Timing: basic +Long Animation Frame Timing: promise resolvers diff --git a/tests/wpt/web-platform-tests/long-animation-frame/tentative/resources/utils.js b/tests/wpt/web-platform-tests/long-animation-frame/tentative/resources/utils.js index 3a121a5a9b5..394d81706cc 100644 --- a/tests/wpt/web-platform-tests/long-animation-frame/tentative/resources/utils.js +++ b/tests/wpt/web-platform-tests/long-animation-frame/tentative/resources/utils.js @@ -4,12 +4,15 @@ setup(() => 'Long animation frames are not supported.')); const very_long_frame_duration = 360; +const no_long_frame_timeout = very_long_frame_duration * 3; +const waiting_for_long_frame_timeout = very_long_frame_duration * 10; function loaf_promise(t) { return new Promise(resolve => { const observer = new PerformanceObserver(entries => { const entry = entries.getEntries()[0]; - if (entry.duration >= very_long_frame_duration) { + // TODO: understand why we need this 5ms epsilon. + if (entry.duration > very_long_frame_duration - 5) { observer.disconnect(); resolve(entry); } @@ -21,8 +24,6 @@ function loaf_promise(t) { }); } -const no_long_frame_timeout = very_long_frame_duration * 2; - function busy_wait(ms_delay = very_long_frame_duration) { const deadline = performance.now() + ms_delay; while (performance.now() < deadline) {} @@ -32,7 +33,7 @@ async function expect_long_frame(cb, t) { await windowLoaded; await new Promise(resolve => t.step_timeout(resolve, 0)); const timeout = new Promise((resolve, reject) => - t.step_timeout(() => resolve("timeout"), no_long_frame_timeout)); + t.step_timeout(() => resolve("timeout"), waiting_for_long_frame_timeout)); const receivedLongFrame = loaf_promise(t); await cb(t); const entry = await Promise.race([ @@ -94,8 +95,9 @@ async function prepare_exec_popup(t, origin) { } function test_loaf_script(cb, name, type, label) { promise_test(async t => { - const [entry, script] = await expect_long_frame_with_script(cb, - script => (script.type === type && script.duration >= very_long_frame_duration), t); + let [entry, script] = []; + [entry, script] = await expect_long_frame_with_script(cb, + script => (script.type === type && script.duration >= very_long_frame_duration), t); assert_true(!!entry, "Entry detected"); assert_equals(script.name, name); diff --git a/tests/wpt/web-platform-tests/mediacapture-record/MediaRecorder-peerconnection-no-sink.https.html b/tests/wpt/web-platform-tests/mediacapture-record/MediaRecorder-peerconnection-no-sink.https.html index 106ad06059c..b806dda898f 100644 --- a/tests/wpt/web-platform-tests/mediacapture-record/MediaRecorder-peerconnection-no-sink.https.html +++ b/tests/wpt/web-platform-tests/mediacapture-record/MediaRecorder-peerconnection-no-sink.https.html @@ -33,8 +33,12 @@ promise_setup(async () => { let combinedSize = 0; // Wait for a small amount of data to appear. Kept small for mobile tests while (combinedSize < 2000) { - const {data} = await new Promise(r => recorder.ondataavailable = r); - combinedSize += data.size; + const event = await Promise.race([ + new Promise(r => recorder.ondataavailable = r), + new Promise(r => t.step_timeout(r, 5000)) + ]); + assert_not_equals(event, undefined, 'ondataavailable should have fired'); + combinedSize += event.data.size; } recorder.stop(); }, `MediaRecorder records from PeerConnection without sinks, ${tag}`); diff --git a/tests/wpt/web-platform-tests/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.tentative.window.js b/tests/wpt/web-platform-tests/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.tentative.window.js index 858d94b26d6..3d333b4485a 100644 --- a/tests/wpt/web-platform-tests/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.tentative.window.js +++ b/tests/wpt/web-platform-tests/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.tentative.window.js @@ -38,9 +38,9 @@ promise_test(async t => { rc1, /*blocked=*/ false, /*url=*/ rc1_url, - /*src=*/ '', - /*id=*/ '', - /*name=*/ '', + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, /*reasons=*/[], /*children=*/[{ 'blocked': true, diff --git a/tests/wpt/web-platform-tests/resources/chromium/webusb-child-test.js b/tests/wpt/web-platform-tests/resources/chromium/webusb-child-test.js index add04fa5825..21412f66b0a 100644 --- a/tests/wpt/web-platform-tests/resources/chromium/webusb-child-test.js +++ b/tests/wpt/web-platform-tests/resources/chromium/webusb-child-test.js @@ -25,7 +25,14 @@ // Wait for a call to GetDevices() to ensure that the interface // handles are forwarded to the parent context. - await navigator.usb.getDevices(); + try { + await navigator.usb.getDevices(); + } catch (e) { + // This can happen in case of, for example, testing usb disallowed + // iframe. + console.error(`getDevices() throws error: ${e.name}: ${e.message}`); + } + messageChannel.port1.postMessage({ type: 'Complete' }); } }; diff --git a/tests/wpt/web-platform-tests/resources/chromium/webxr-test.js b/tests/wpt/web-platform-tests/resources/chromium/webxr-test.js index db61e1ce4d6..679343764a0 100644 --- a/tests/wpt/web-platform-tests/resources/chromium/webxr-test.js +++ b/tests/wpt/web-platform-tests/resources/chromium/webxr-test.js @@ -1,4 +1,5 @@ import * as vrMojom from '/gen/device/vr/public/mojom/vr_service.mojom.m.js'; +import * as xrSessionMojom from '/gen/device/vr/public/mojom/xr_session.mojom.m.js'; import {GamepadHand, GamepadMapping} from '/gen/device/gamepad/public/mojom/gamepad.mojom.m.js'; // This polyfill library implements the WebXR Test API as specified here: @@ -322,25 +323,25 @@ class MockRuntime { // Mapping from string feature names to the corresponding mojo types. // This is exposed as a member for extensibility. static _featureToMojoMap = { - 'viewer': vrMojom.XRSessionFeature.REF_SPACE_VIEWER, - 'local': vrMojom.XRSessionFeature.REF_SPACE_LOCAL, - 'local-floor': vrMojom.XRSessionFeature.REF_SPACE_LOCAL_FLOOR, - 'bounded-floor': vrMojom.XRSessionFeature.REF_SPACE_BOUNDED_FLOOR, - 'unbounded': vrMojom.XRSessionFeature.REF_SPACE_UNBOUNDED, - 'hit-test': vrMojom.XRSessionFeature.HIT_TEST, - 'dom-overlay': vrMojom.XRSessionFeature.DOM_OVERLAY, - 'light-estimation': vrMojom.XRSessionFeature.LIGHT_ESTIMATION, - 'anchors': vrMojom.XRSessionFeature.ANCHORS, - 'depth-sensing': vrMojom.XRSessionFeature.DEPTH, - 'secondary-views': vrMojom.XRSessionFeature.SECONDARY_VIEWS, - 'camera-access': vrMojom.XRSessionFeature.CAMERA_ACCESS, - 'layers': vrMojom.XRSessionFeature.LAYERS, + 'viewer': xrSessionMojom.XRSessionFeature.REF_SPACE_VIEWER, + 'local': xrSessionMojom.XRSessionFeature.REF_SPACE_LOCAL, + 'local-floor': xrSessionMojom.XRSessionFeature.REF_SPACE_LOCAL_FLOOR, + 'bounded-floor': xrSessionMojom.XRSessionFeature.REF_SPACE_BOUNDED_FLOOR, + 'unbounded': xrSessionMojom.XRSessionFeature.REF_SPACE_UNBOUNDED, + 'hit-test': xrSessionMojom.XRSessionFeature.HIT_TEST, + 'dom-overlay': xrSessionMojom.XRSessionFeature.DOM_OVERLAY, + 'light-estimation': xrSessionMojom.XRSessionFeature.LIGHT_ESTIMATION, + 'anchors': xrSessionMojom.XRSessionFeature.ANCHORS, + 'depth-sensing': xrSessionMojom.XRSessionFeature.DEPTH, + 'secondary-views': xrSessionMojom.XRSessionFeature.SECONDARY_VIEWS, + 'camera-access': xrSessionMojom.XRSessionFeature.CAMERA_ACCESS, + 'layers': xrSessionMojom.XRSessionFeature.LAYERS, }; static _sessionModeToMojoMap = { - "inline": vrMojom.XRSessionMode.kInline, - "immersive-vr": vrMojom.XRSessionMode.kImmersiveVr, - "immersive-ar": vrMojom.XRSessionMode.kImmersiveAr, + "inline": xrSessionMojom.XRSessionMode.kInline, + "immersive-vr": xrSessionMojom.XRSessionMode.kImmersiveVr, + "immersive-ar": xrSessionMojom.XRSessionMode.kImmersiveAr, }; static _environmentBlendModeToMojoMap = { @@ -700,10 +701,10 @@ class MockRuntime { if (blendMode in MockRuntime._environmentBlendModeToMojoMap) { return MockRuntime._environmentBlendModeToMojoMap[blendMode]; } else { - if (this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) { + if (this.supportedModes_.includes(xrSessionMojom.XRSessionMode.kImmersiveAr)) { return vrMojom.XREnvironmentBlendMode.kAdditive; } else if (this.supportedModes_.includes( - vrMojom.XRSessionMode.kImmersiveVr)) { + xrSessionMojom.XRSessionMode.kImmersiveVr)) { return vrMojom.XREnvironmentBlendMode.kOpaque; } } @@ -852,7 +853,7 @@ class MockRuntime { if (feature in MockRuntime._featureToMojoMap) { return MockRuntime._featureToMojoMap[feature]; } else { - return vrMojom.XRSessionFeature.INVALID; + return xrSessionMojom.XRSessionFeature.INVALID; } } @@ -860,7 +861,7 @@ class MockRuntime { for (let i = 0; i < supportedFeatures.length; i++) { const feature = convertFeatureToMojom(supportedFeatures[i]); - if (feature !== vrMojom.XRSessionFeature.INVALID) { + if (feature !== xrSessionMojom.XRSessionFeature.INVALID) { this.supportedFeatures_.push(feature); } } @@ -917,7 +918,7 @@ class MockRuntime { this.primaryViews_[i].mojoFromView = this._getMojoFromViewerWithOffset(this.primaryViews_[i].viewOffset); } - if (this.enabledFeatures_.includes(vrMojom.XRSessionFeature.SECONDARY_VIEWS)) { + if (this.enabledFeatures_.includes(xrSessionMojom.XRSessionFeature.SECONDARY_VIEWS)) { for (let i = 0; i < this.secondaryViews_.length; i++) { this.secondaryViews_[i].mojoFromView = this._getMojoFromViewerWithOffset(this.secondaryViews_[i].viewOffset); @@ -960,7 +961,7 @@ class MockRuntime { resolve({frameData}); }; - if(this.sessionOptions_.mode == vrMojom.XRSessionMode.kInline) { + if(this.sessionOptions_.mode == xrSessionMojom.XRSessionMode.kInline) { // Inline sessions should not have a delay introduced since it causes them // to miss a vsync blink-side and delays propagation of changes that happened // within a rAFcb by one frame (e.g. setViewerOrigin() calls would take 2 frames @@ -988,7 +989,7 @@ class MockRuntime { // XREnvironmentIntegrationProvider implementation: subscribeToHitTest(nativeOriginInformation, entityTypes, ray) { - if (!this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) { + if (!this.supportedModes_.includes(xrSessionMojom.XRSessionMode.kImmersiveAr)) { // Reject outside of AR. return Promise.resolve({ result : vrMojom.SubscribeToHitTestResult.FAILURE_GENERIC, @@ -1029,7 +1030,7 @@ class MockRuntime { } subscribeToHitTestForTransientInput(profileName, entityTypes, ray){ - if (!this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) { + if (!this.supportedModes_.includes(xrSessionMojom.XRSessionMode.kImmersiveAr)) { // Reject outside of AR. return Promise.resolve({ result : vrMojom.SubscribeToHitTestResult.FAILURE_GENERIC, @@ -1215,7 +1216,7 @@ class MockRuntime { defaultFramebufferScale: this.defaultFramebufferScale_, supportsViewportScaling: true, depthConfiguration: - enabled_features.includes(vrMojom.XRSessionFeature.DEPTH) ? { + enabled_features.includes(xrSessionMojom.XRSessionFeature.DEPTH) ? { depthUsage: vrMojom.XRDepthUsage.kCPUOptimized, depthDataFormat: vrMojom.XRDepthDataFormat.kLuminanceAlpha, } : null, @@ -1234,8 +1235,8 @@ class MockRuntime { _runtimeSupportsSession(options) { let result = this.supportedModes_.includes(options.mode); - if (options.requiredFeatures.includes(vrMojom.XRSessionFeature.DEPTH) - || options.optionalFeatures.includes(vrMojom.XRSessionFeature.DEPTH)) { + if (options.requiredFeatures.includes(xrSessionMojom.XRSessionFeature.DEPTH) + || options.optionalFeatures.includes(xrSessionMojom.XRSessionFeature.DEPTH)) { result &= options.depthOptions.usagePreferences.includes(vrMojom.XRDepthUsage.kCPUOptimized); result &= options.depthOptions.dataFormatPreferences.includes(vrMojom.XRDepthDataFormat.kLuminanceAlpha); } @@ -1273,7 +1274,7 @@ class MockRuntime { // Modifies passed in frameData to add anchor information. _calculateAnchorInformation(frameData) { - if (!this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) { + if (!this.supportedModes_.includes(xrSessionMojom.XRSessionMode.kImmersiveAr)) { return; } @@ -1301,11 +1302,11 @@ class MockRuntime { // Modifies passed in frameData to add anchor information. _calculateDepthInformation(frameData) { - if (!this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) { + if (!this.supportedModes_.includes(xrSessionMojom.XRSessionMode.kImmersiveAr)) { return; } - if (!this.enabledFeatures_.includes(vrMojom.XRSessionFeature.DEPTH)) { + if (!this.enabledFeatures_.includes(xrSessionMojom.XRSessionFeature.DEPTH)) { return; } @@ -1349,7 +1350,7 @@ class MockRuntime { // Modifies passed in frameData to add hit test results. _calculateHitTestResults(frameData) { - if (!this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) { + if (!this.supportedModes_.includes(xrSessionMojom.XRSessionMode.kImmersiveAr)) { return; } diff --git a/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-abort.any.js b/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-abort.any.js new file mode 100644 index 00000000000..610c8a9819a --- /dev/null +++ b/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-abort.any.js @@ -0,0 +1,20 @@ +'use strict'; + +promise_test(t => { + const signal = AbortSignal.abort(); + return scheduler.postTask(async () => { + const p = scheduler.yield({signal}); + await promise_rejects_dom(t, 'AbortError', p); + }); +}, 'yield() with an aborted signal'); + +promise_test(t => { + const controller = new TaskController(); + const signal = controller.signal; + return scheduler.postTask(async () => { + scheduler.postTask(async () => {controller.abort();}, {priority: 'user-blocking'}); + assert_false(signal.aborted); + const p = scheduler.yield({signal}); + await promise_rejects_dom(t, 'AbortError', p); + }); +}, 'yield() aborted in a separate task'); diff --git a/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-priority-posttask.any.js b/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-priority-posttask.any.js new file mode 100644 index 00000000000..dae3b939fd6 --- /dev/null +++ b/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-priority-posttask.any.js @@ -0,0 +1,111 @@ +'use strict'; + +// Posts a postTask task with `yieldyTaskParams` that yields 3 times using +// `yieldParams`, then posts 2 tasks of each priority, in descending order. +// +// Returns {tasks, ids} where `tasks` is an array of promises returned by +// postTask and `ids` is an array of task ids appended to by the scheduled +// tasks. +function postTestTasks(yieldyTaskParams, yieldParams) { + const tasks = []; + const ids = []; + + tasks.push(scheduler.postTask(async () => { + ids.push('y0'); + for (let i = 1; i < 4; i++) { + await scheduler.yield(yieldParams); + ids.push('y' + i); + } + }, yieldyTaskParams)); + + tasks.push( + scheduler.postTask(() => {ids.push('ub1')}, {priority: 'user-blocking'})); + tasks.push( + scheduler.postTask(() => {ids.push('ub2')}, {priority: 'user-blocking'})); + tasks.push( + scheduler.postTask(() => {ids.push('uv1')}, {priority: 'user-visible'})); + tasks.push( + scheduler.postTask(() => {ids.push('uv2')}, {priority: 'user-visible'})); + tasks.push( + scheduler.postTask(() => {ids.push('bg1')}, {priority: 'background'})); + tasks.push( + scheduler.postTask(() => {ids.push('bg2')}, {priority: 'background'})); + return {tasks, ids}; +} + +// Expected task orders for `postTestTasks` tasks. +const taskOrders = { + 'user-blocking': 'y0,y1,y2,y3,ub1,ub2,uv1,uv2,bg1,bg2', + 'user-visible': 'ub1,ub2,y0,y1,y2,y3,uv1,uv2,bg1,bg2', + 'background': 'ub1,ub2,uv1,uv2,y0,y1,y2,y3,bg1,bg2', +}; + +const priorityConfigs = [ + {options: {}, expected: taskOrders['user-visible']}, + {options: {priority: 'user-visible'}, expected: taskOrders['user-visible']}, + {options: {priority: 'user-blocking'}, expected: taskOrders['user-blocking']}, + {options: {priority: 'background'}, expected: taskOrders['background']}, +]; + +const fixedPrioritySignals = { + 'user-blocking': (new TaskController({priority: 'user-blocking'})).signal, + 'user-visible': (new TaskController({priority: 'user-visible'})).signal, + 'background': (new TaskController({priority: 'background'})).signal, +}; + +const signalConfigs = [ + { + options: {signal: fixedPrioritySignals['user-visible']}, + expected: taskOrders['user-visible'] + }, + { + options: {signal: fixedPrioritySignals['user-blocking']}, + expected: taskOrders['user-blocking'] + }, + { + options: {signal: fixedPrioritySignals['background']}, + expected: taskOrders['background'] + }, +]; + +promise_test(async t => { + for (const config of priorityConfigs) { + const {tasks, ids} = postTestTasks(config.options, config.options); + await Promise.all(tasks); + assert_equals(ids.join(), config.expected); + } +}, 'yield() with postTask tasks (priority option)'); + +promise_test(async t => { + for (const config of signalConfigs) { + const {tasks, ids} = postTestTasks(config.options, config.options); + await Promise.all(tasks); + assert_equals(ids.join(), config.expected); + } +}, 'yield() with postTask tasks (signal option)'); + +promise_test(async t => { + const expected = 'y0,ub1,ub2,uv1,uv2,y1,y2,y3,bg1,bg2'; + const {tasks, ids} = postTestTasks( + {priority: 'user-blocking'}, {priority: 'background'}); + await Promise.all(tasks); + assert_equals(ids.join(), expected); +}, 'yield() with different priority from task (priority)'); + +promise_test(async t => { + const expected = 'y0,ub1,ub2,uv1,uv2,y1,y2,y3,bg1,bg2'; + const bgSignal = (new TaskController({priority: 'background'})).signal; + const {tasks, ids} = + postTestTasks({priority: 'user-blocking'}, {signal: bgSignal}); + await Promise.all(tasks); + assert_equals(ids.join(), expected); +}, 'yield() with different priority from task (signal)'); + +promise_test(async t => { + const bgSignal = (new TaskController({priority: 'background'})).signal; + const {tasks, ids} = postTestTasks( + {priority: 'user-blocking'}, + {signal: bgSignal, priority: 'user-blocking'}); + await Promise.all(tasks); + assert_equals(ids.join(), taskOrders['user-blocking']); +}, 'yield() priority overrides signal'); diff --git a/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-priority-timers.any.js b/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-priority-timers.any.js new file mode 100644 index 00000000000..81a2f9b0251 --- /dev/null +++ b/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-priority-timers.any.js @@ -0,0 +1,82 @@ +'use strict'; + +// Queues a zero ms timer that yields 3 times using `yieldParams`, then posts 2 +// more 0 ms timers. +// +// Returns {tasks, ids} where `tasks` is an array of promises associated with +// the timers and `ids` is an array of task ids appended to by the scheduled +// tasks. +function postTestTasks(yieldParams) { + const tasks = []; + const ids = []; + + tasks.push(new Promise(resolve => { + setTimeout(async () => { + ids.push('t1'); + for (let i = 1; i < 4; i++) { + await scheduler.yield(yieldParams); + ids.push('y' + i); + } + resolve(); + }); + })); + + tasks.push(new Promise(resolve => { + setTimeout(() => { ids.push('t2'); resolve(); }); + })); + tasks.push(new Promise(resolve => { + setTimeout(() => { ids.push('t3'); resolve(); }); + })); + return {tasks, ids}; +} + +// Expected task orders for `postTestTasks` tasks. +const taskOrders = { + 'user-blocking': 't1,y1,y2,y3,t2,t3', + 'user-visible': 't1,y1,y2,y3,t2,t3', + 'background': 't1,t2,t3,y1,y2,y3', +}; + +const priorityConfigs = [ + {options: {}, expected: taskOrders['user-visible']}, + {options: {priority: 'user-visible'}, expected: taskOrders['user-visible']}, + {options: {priority: 'user-blocking'}, expected: taskOrders['user-blocking']}, + {options: {priority: 'background'}, expected: taskOrders['background']}, +]; + +const fixedPrioritySignals = { + 'user-blocking': (new TaskController({priority: 'user-blocking'})).signal, + 'user-visible': (new TaskController({priority: 'user-visible'})).signal, + 'background': (new TaskController({priority: 'background'})).signal, +}; + +const signalConfigs = [ + { + options: {signal: fixedPrioritySignals['user-visible']}, + expected: taskOrders['user-visible'] + }, + { + options: {signal: fixedPrioritySignals['user-blocking']}, + expected: taskOrders['user-blocking'] + }, + { + options: {signal: fixedPrioritySignals['background']}, + expected: taskOrders['background'] + }, +]; + +promise_test(async t => { + for (const config of priorityConfigs) { + const {tasks, ids} = postTestTasks(config.options); + await Promise.all(tasks); + assert_equals(ids.join(), config.expected); + } +}, 'yield() with timer tasks (priority option)'); + +promise_test(async t => { + for (const config of signalConfigs) { + const {tasks, ids} = postTestTasks(config.options); + await Promise.all(tasks); + assert_equals(ids.join(), config.expected); + } +}, 'yield() with timer tasks (signal option)'); diff --git a/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-then-detach.html b/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-then-detach.html new file mode 100644 index 00000000000..835f9e7a62d --- /dev/null +++ b/tests/wpt/web-platform-tests/scheduler/tentative/yield/yield-then-detach.html @@ -0,0 +1,26 @@ + +Scheduler: yield in Detached Scheduler + + + + + diff --git a/tests/wpt/web-platform-tests/scroll-animations/view-timelines/timeline-offset-in-keyframe.html b/tests/wpt/web-platform-tests/scroll-animations/view-timelines/timeline-offset-in-keyframe.html new file mode 100644 index 00000000000..62a8d1387d3 --- /dev/null +++ b/tests/wpt/web-platform-tests/scroll-animations/view-timelines/timeline-offset-in-keyframe.html @@ -0,0 +1,263 @@ + + + + + + + + + +Animation range and delay + + + +
+
+
+ + + diff --git a/tests/wpt/web-platform-tests/scroll-animations/view-timelines/view-timeline-get-set-range.html b/tests/wpt/web-platform-tests/scroll-animations/view-timelines/view-timeline-get-set-range.html new file mode 100644 index 00000000000..e80ef57afe0 --- /dev/null +++ b/tests/wpt/web-platform-tests/scroll-animations/view-timelines/view-timeline-get-set-range.html @@ -0,0 +1,120 @@ + + + +View timeline delay + + + + + + + + + +
+
+
+
+
+
+
+ + + diff --git a/tests/wpt/web-platform-tests/serial/getPorts/sandboxed_iframe.https.window.js b/tests/wpt/web-platform-tests/serial/getPorts/sandboxed_iframe.https.window.js index 8fae11ccfe5..0a99f75aae6 100644 --- a/tests/wpt/web-platform-tests/serial/getPorts/sandboxed_iframe.https.window.js +++ b/tests/wpt/web-platform-tests/serial/getPorts/sandboxed_iframe.https.window.js @@ -1,8 +1,7 @@ 'use strict'; -let iframe = document.createElement('iframe'); - -promise_test(async () => { +promise_test(async (t) => { + let iframe = document.createElement('iframe'); await new Promise(resolve => { iframe.src = '../resources/open-in-iframe.html'; iframe.sandbox.add('allow-scripts'); @@ -12,11 +11,10 @@ promise_test(async () => { }); await new Promise(resolve => { - iframe.contentWindow.postMessage({type: 'GetPorts'}, '*'); - - window.addEventListener('message', (messageEvent) => { - assert_equals('Success', messageEvent.data); + window.addEventListener('message', t.step_func(messageEvent => { + assert_equals(messageEvent.data, 'Success'); resolve(); - }); + })); + iframe.contentWindow.postMessage({type: 'GetPorts'}, '*'); }); }, 'GetPorts from a sandboxed iframe is valid.'); diff --git a/tests/wpt/web-platform-tests/serial/requestPort/sandboxed_iframe.https.window.js b/tests/wpt/web-platform-tests/serial/requestPort/sandboxed_iframe.https.window.js index 6e169510a0b..f6d7abad103 100644 --- a/tests/wpt/web-platform-tests/serial/requestPort/sandboxed_iframe.https.window.js +++ b/tests/wpt/web-platform-tests/serial/requestPort/sandboxed_iframe.https.window.js @@ -1,8 +1,7 @@ 'use strict'; -let iframe = document.createElement('iframe'); - -promise_test(async () => { +promise_test(async (t) => { + let iframe = document.createElement('iframe'); await new Promise(resolve => { iframe.src = '../resources/open-in-iframe.html'; iframe.sandbox.add('allow-scripts'); @@ -12,15 +11,14 @@ promise_test(async () => { }); await new Promise(resolve => { - iframe.contentWindow.postMessage({type: 'RequestPort'}, '*'); - - window.addEventListener('message', (messageEvent) => { + window.addEventListener('message', t.step_func(messageEvent => { // The failure message of no device chosen is expected. The point here is // to validate not failing because of a sandboxed iframe. assert_equals( 'FAIL: NotFoundError: Failed to execute \'requestPort\' on \'Serial\': No port selected by the user.', messageEvent.data); resolve(); - }); + })); + iframe.contentWindow.postMessage({type: 'RequestPort'}, '*'); }); }, 'RequestPort from a sandboxed iframe is valid.'); diff --git a/tests/wpt/web-platform-tests/serial/serial-disabled-by-permissions-policy.https.sub.html b/tests/wpt/web-platform-tests/serial/serial-disabled-by-permissions-policy.https.sub.html index 53646c526c8..251bf84f475 100644 --- a/tests/wpt/web-platform-tests/serial/serial-disabled-by-permissions-policy.https.sub.html +++ b/tests/wpt/web-platform-tests/serial/serial-disabled-by-permissions-policy.https.sub.html @@ -2,6 +2,8 @@ + + diff --git a/tests/wpt/web-platform-tests/speculation-rules/prerender/resources/workers-in-cross-origin-iframe.html b/tests/wpt/web-platform-tests/speculation-rules/prerender/resources/workers-in-cross-origin-iframe.html new file mode 100644 index 00000000000..8f27533ed17 --- /dev/null +++ b/tests/wpt/web-platform-tests/speculation-rules/prerender/resources/workers-in-cross-origin-iframe.html @@ -0,0 +1,15 @@ + +Construction of Web Workers is deferred + + + + + diff --git a/tests/wpt/web-platform-tests/speculation-rules/prerender/workers-in-cross-origin-iframe.html b/tests/wpt/web-platform-tests/speculation-rules/prerender/workers-in-cross-origin-iframe.html new file mode 100644 index 00000000000..8d79f43a4ed --- /dev/null +++ b/tests/wpt/web-platform-tests/speculation-rules/prerender/workers-in-cross-origin-iframe.html @@ -0,0 +1,51 @@ + +Construction of Web Workers in cross-origin iframe is deferred + + + + + + + + + + + + diff --git a/tests/wpt/web-platform-tests/tools/localpaths.py b/tests/wpt/web-platform-tests/tools/localpaths.py index a7b887885ba..e8095ac69d5 100644 --- a/tests/wpt/web-platform-tests/tools/localpaths.py +++ b/tests/wpt/web-platform-tests/tools/localpaths.py @@ -21,6 +21,7 @@ sys.path.insert(0, os.path.join(here, "third_party", "pytest")) sys.path.insert(0, os.path.join(here, "third_party", "pytest", "src")) sys.path.insert(0, os.path.join(here, "third_party", "pytest-asyncio")) sys.path.insert(0, os.path.join(here, "third_party", "six")) +sys.path.insert(0, os.path.join(here, "third_party", "typing_extensions", "src")) sys.path.insert(0, os.path.join(here, "third_party", "webencodings")) sys.path.insert(0, os.path.join(here, "third_party", "h2")) sys.path.insert(0, os.path.join(here, "third_party", "hpack")) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/CHANGELOG.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/CHANGELOG.rst new file mode 100644 index 00000000000..8de226c4f9b --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/CHANGELOG.rst @@ -0,0 +1,169 @@ +========= +Changelog +========= + +0.19.0 (22-07-13) +================= +- BREAKING: The default ``asyncio_mode`` is now *strict*. `#293 `_ +- Removes `setup.py` since all relevant configuration is present `setup.cfg`. Users requiring an editable installation of pytest-asyncio need to use pip v21.1 or newer. `#283 `_ +- Declare support for Python 3.11. + +0.18.3 (22-03-25) +================= +- Adds `pytest-trio `_ to the test dependencies +- Fixes a bug that caused pytest-asyncio to try to set up async pytest_trio fixtures in strict mode. `#298 `_ + +0.18.2 (22-03-03) +================= +- Fix asyncio auto mode not marking static methods. `#295 `_ +- Fix a compatibility issue with Hypothesis 6.39.0. `#302 `_ + +0.18.1 (22-02-10) +================= +- Fixes a regression that prevented async fixtures from working in synchronous tests. `#286 `_ + +0.18.0 (22-02-07) +================= + +- Raise a warning if @pytest.mark.asyncio is applied to non-async function. `#275 `_ +- Support parametrized ``event_loop`` fixture. `#278 `_ + +0.17.2 (22-01-17) +================= + +- Require ``typing-extensions`` on Python<3.8 only. `#269 `_ +- Fix a regression in tests collection introduced by 0.17.1, the plugin works fine with non-python tests again. `#267 `_ + + +0.17.1 (22-01-16) +================= +- Fixes a bug that prevents async Hypothesis tests from working without explicit ``asyncio`` marker when ``--asyncio-mode=auto`` is set. `#258 `_ +- Fixed a bug that closes the default event loop if the loop doesn't exist `#257 `_ +- Added type annotations. `#198 `_ +- Show asyncio mode in pytest report headers. `#266 `_ +- Relax ``asyncio_mode`` type definition; it allows to support pytest 6.1+. `#262 `_ + +0.17.0 (22-01-13) +================= +- `pytest-asyncio` no longer alters existing event loop policies. `#168 `_, `#188 `_ +- Drop support for Python 3.6 +- Fixed an issue when pytest-asyncio was used in combination with `flaky` or inherited asynchronous Hypothesis tests. `#178 `_ `#231 `_ +- Added `flaky `_ to test dependencies +- Added ``unused_udp_port`` and ``unused_udp_port_factory`` fixtures (similar to ``unused_tcp_port`` and ``unused_tcp_port_factory`` counterparts. `#99 `_ +- Added the plugin modes: *strict*, *auto*, and *legacy*. See `documentation `_ for details. `#125 `_ +- Correctly process ``KeyboardInterrupt`` during async fixture setup phase `#219 `_ + +0.16.0 (2021-10-16) +=================== +- Add support for Python 3.10 + +0.15.1 (2021-04-22) +=================== +- Hotfix for errors while closing event loops while replacing them. + `#209 `_ + `#210 `_ + +0.15.0 (2021-04-19) +=================== +- Add support for Python 3.9 +- Abandon support for Python 3.5. If you still require support for Python 3.5, please use pytest-asyncio v0.14 or earlier. +- Set ``unused_tcp_port_factory`` fixture scope to 'session'. + `#163 `_ +- Properly close event loops when replacing them. + `#208 `_ + +0.14.0 (2020-06-24) +=================== +- Fix `#162 `_, and ``event_loop`` fixture behavior now is coherent on all scopes. + `#164 `_ + +0.12.0 (2020-05-04) +=================== +- Run the event loop fixture as soon as possible. This helps with fixtures that have an implicit dependency on the event loop. + `#156 `_ + +0.11.0 (2020-04-20) +=================== +- Test on 3.8, drop 3.3 and 3.4. Stick to 0.10 for these versions. + `#152 `_ +- Use the new Pytest 5.4.0 Function API. We therefore depend on pytest >= 5.4.0. + `#142 `_ +- Better ``pytest.skip`` support. + `#126 `_ + +0.10.0 (2019-01-08) +==================== +- ``pytest-asyncio`` integrates with `Hypothesis `_ + to support ``@given`` on async test functions using ``asyncio``. + `#102 `_ +- Pytest 4.1 support. + `#105 `_ + +0.9.0 (2018-07-28) +================== +- Python 3.7 support. +- Remove ``event_loop_process_pool`` fixture and + ``pytest.mark.asyncio_process_pool`` marker (see + https://bugs.python.org/issue34075 for deprecation and removal details) + +0.8.0 (2017-09-23) +================== +- Improve integration with other packages (like aiohttp) with more careful event loop handling. + `#64 `_ + +0.7.0 (2017-09-08) +================== +- Python versions pre-3.6 can use the async_generator library for async fixtures. + `#62 ` + +0.6.0 (2017-05-28) +================== +- Support for Python versions pre-3.5 has been dropped. +- ``pytestmark`` now works on both module and class level. +- The ``forbid_global_loop`` parameter has been removed. +- Support for async and async gen fixtures has been added. + `#45 `_ +- The deprecation warning regarding ``asyncio.async()`` has been fixed. + `#51 `_ + +0.5.0 (2016-09-07) +================== +- Introduced a changelog. + `#31 `_ +- The ``event_loop`` fixture is again responsible for closing itself. + This makes the fixture slightly harder to correctly override, but enables + other fixtures to depend on it correctly. + `#30 `_ +- Deal with the event loop policy by wrapping a special pytest hook, + ``pytest_fixture_setup``. This allows setting the policy before fixtures + dependent on the ``event_loop`` fixture run, thus allowing them to take + advantage of the ``forbid_global_loop`` parameter. As a consequence of this, + we now depend on pytest 3.0. + `#29 `_ + +0.4.1 (2016-06-01) +================== +- Fix a bug preventing the propagation of exceptions from the plugin. + `#25 `_ + +0.4.0 (2016-05-30) +================== +- Make ``event_loop`` fixtures simpler to override by closing them in the + plugin, instead of directly in the fixture. + `#21 `_ +- Introduce the ``forbid_global_loop`` parameter. + `#21 `_ + +0.3.0 (2015-12-19) +================== +- Support for Python 3.5 ``async``/``await`` syntax. + `#17 `_ + +0.2.0 (2015-08-01) +================== +- ``unused_tcp_port_factory`` fixture. + `#10 `_ + +0.1.1 (2015-04-23) +================== +Initial release. diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/LICENSE b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/LICENSE index e06d2081865..5c304d1a4a7 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/LICENSE +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/LICENSE @@ -199,4 +199,3 @@ Apache License WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/MANIFEST.in b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/MANIFEST.in new file mode 100644 index 00000000000..fdf813e9155 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/MANIFEST.in @@ -0,0 +1,5 @@ +include CHANGELOG.rst + +recursive-exclude .github * +exclude .gitignore +exclude .pre-commit-config.yaml diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/Makefile b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/Makefile new file mode 100644 index 00000000000..2b0216f99ec --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/Makefile @@ -0,0 +1,39 @@ +.PHONY: clean clean-build clean-pyc clean-test lint test + +clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts + +clean-build: ## remove build artifacts + rm -fr build/ + rm -fr dist/ + rm -fr .eggs/ + find . -name '*.egg-info' -exec rm -fr {} + + find . -name '*.egg' -exec rm -f {} + + +clean-pyc: ## remove Python file artifacts + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + find . -name '__pycache__' -exec rm -fr {} + + +clean-test: ## remove test and coverage artifacts + rm -fr .tox/ + rm -f .coverage + rm -fr htmlcov/ + +lint: +# CI env-var is set by GitHub actions +ifdef CI + python -m pre_commit run --all-files --show-diff-on-failure +else + python -m pre_commit run --all-files +endif + python -m mypy pytest_asyncio --show-error-codes + +test: + coverage run -m pytest tests + coverage xml + coverage report + +install: + pip install -U pre-commit + pre-commit install diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/PKG-INFO b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/PKG-INFO index bb7611bc1a7..19acaa4d513 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/PKG-INFO +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/PKG-INFO @@ -1,302 +1,285 @@ Metadata-Version: 2.1 Name: pytest-asyncio -Version: 0.14.0 -Summary: Pytest support for asyncio. +Version: 0.19.0 +Summary: Pytest support for asyncio Home-page: https://github.com/pytest-dev/pytest-asyncio -Author: Tin Tvrtković +Author: Tin Tvrtković Author-email: tinchester@gmail.com License: Apache 2.0 -Description: pytest-asyncio: pytest support for asyncio - ========================================== - - .. image:: https://img.shields.io/pypi/v/pytest-asyncio.svg - :target: https://pypi.python.org/pypi/pytest-asyncio - .. image:: https://travis-ci.org/pytest-dev/pytest-asyncio.svg?branch=master - :target: https://travis-ci.org/pytest-dev/pytest-asyncio - .. image:: https://coveralls.io/repos/pytest-dev/pytest-asyncio/badge.svg - :target: https://coveralls.io/r/pytest-dev/pytest-asyncio - .. image:: https://img.shields.io/pypi/pyversions/pytest-asyncio.svg - :target: https://github.com/pytest-dev/pytest-asyncio - :alt: Supported Python versions - - pytest-asyncio is an Apache2 licensed library, written in Python, for testing - asyncio code with pytest. - - asyncio code is usually written in the form of coroutines, which makes it - slightly more difficult to test using normal testing tools. pytest-asyncio - provides useful fixtures and markers to make testing easier. - - .. code-block:: python - - @pytest.mark.asyncio - async def test_some_asyncio_code(): - res = await library.do_something() - assert b'expected result' == res - - pytest-asyncio has been strongly influenced by pytest-tornado_. - - .. _pytest-tornado: https://github.com/eugeniy/pytest-tornado - - Features - -------- - - - fixtures for creating and injecting versions of the asyncio event loop - - fixtures for injecting unused tcp ports - - pytest markers for treating tests as asyncio coroutines - - easy testing with non-default event loops - - support for `async def` fixtures and async generator fixtures - - Installation - ------------ - - To install pytest-asyncio, simply: - - .. code-block:: bash - - $ pip install pytest-asyncio - - This is enough for pytest to pick up pytest-asyncio. - - Fixtures - -------- - - ``event_loop`` - ~~~~~~~~~~~~~~ - Creates and injects a new instance of the default asyncio event loop. By - default, the loop will be closed at the end of the test (i.e. the default - fixture scope is ``function``). - - Note that just using the ``event_loop`` fixture won't make your test function - a coroutine. You'll need to interact with the event loop directly, using methods - like ``event_loop.run_until_complete``. See the ``pytest.mark.asyncio`` marker - for treating test functions like coroutines. - - Simply using this fixture will not set the generated event loop as the - default asyncio event loop, or change the asyncio event loop policy in any way. - Use ``pytest.mark.asyncio`` for this purpose. - - .. code-block:: python - - def test_http_client(event_loop): - url = 'http://httpbin.org/get' - resp = event_loop.run_until_complete(http_client(url)) - assert b'HTTP/1.1 200 OK' in resp - - This fixture can be easily overridden in any of the standard pytest locations - (e.g. directly in the test file, or in ``conftest.py``) to use a non-default - event loop. This will take effect even if you're using the - ``pytest.mark.asyncio`` marker and not the ``event_loop`` fixture directly. - - .. code-block:: python - - @pytest.fixture - def event_loop(): - loop = MyCustomLoop() - yield loop - loop.close() - - If the ``pytest.mark.asyncio`` marker is applied, a pytest hook will - ensure the produced loop is set as the default global loop. - Fixtures depending on the ``event_loop`` fixture can expect the policy to be properly modified when they run. - - ``unused_tcp_port`` - ~~~~~~~~~~~~~~~~~~~ - Finds and yields a single unused TCP port on the localhost interface. Useful for - binding temporary test servers. - - ``unused_tcp_port_factory`` - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - A callable which returns a different unused TCP port each invocation. Useful - when several unused TCP ports are required in a test. - - .. code-block:: python - - def a_test(unused_tcp_port_factory): - port1, port2 = unused_tcp_port_factory(), unused_tcp_port_factory() - ... - - Async fixtures - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Asynchronous fixtures are defined just like ordinary pytest fixtures, except they should be coroutines or asynchronous generators. - - .. code-block:: python3 - - @pytest.fixture - async def async_gen_fixture(): - await asyncio.sleep(0.1) - yield 'a value' - - @pytest.fixture(scope='module') - async def async_fixture(): - return await asyncio.sleep(0.1) - - All scopes are supported, but if you use a non-function scope you will need - to redefine the ``event_loop`` fixture to have the same or broader scope. - Async fixtures need the event loop, and so must have the same or narrower scope - than the ``event_loop`` fixture. - - If you want to do this with Python 3.5, the ``yield`` statement must be replaced with ``await yield_()`` and the coroutine - function must be decorated with ``@async_generator``, like so: - - .. code-block:: python3 - - from async_generator import yield_, async_generator - - @pytest.fixture - @async_generator - async def async_gen_fixture(): - await asyncio.sleep(0.1) - await yield_('a value') - - - Markers - ------- - - ``pytest.mark.asyncio`` - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Mark your test coroutine with this marker and pytest will execute it as an - asyncio task using the event loop provided by the ``event_loop`` fixture. See - the introductory section for an example. - - The event loop used can be overriden by overriding the ``event_loop`` fixture - (see above). - - In order to make your test code a little more concise, the pytest |pytestmark|_ - feature can be used to mark entire modules or classes with this marker. - Only test coroutines will be affected (by default, coroutines prefixed by - ``test_``), so, for example, fixtures are safe to define. - - .. code-block:: python - - import asyncio - import pytest - - # All test coroutines will be treated as marked. - pytestmark = pytest.mark.asyncio - - async def test_example(event_loop): - """No marker!""" - await asyncio.sleep(0, loop=event_loop) - - .. |pytestmark| replace:: ``pytestmark`` - .. _pytestmark: http://doc.pytest.org/en/latest/example/markers.html#marking-whole-classes-or-modules - - Changelog - --------- - 0.13.0 (2020-06-24) - ~~~~~~~~~~~~~~~~~~~ - - Fix `#162 `_, and ``event_loop`` fixture behavior now is coherent on all scopes. - `#164 `_ - - 0.12.0 (2020-05-04) - ~~~~~~~~~~~~~~~~~~~ - - Run the event loop fixture as soon as possible. This helps with fixtures that have an implicit dependency on the event loop. - `#156 `_ - - 0.11.0 (2020-04-20) - ~~~~~~~~~~~~~~~~~~~ - - Test on 3.8, drop 3.3 and 3.4. Stick to 0.10 for these versions. - `#152 `_ - - Use the new Pytest 5.4.0 Function API. We therefore depend on pytest >= 5.4.0. - `#142 `_ - - Better ``pytest.skip`` support. - `#126 `_ - - 0.10.0 (2019-01-08) - ~~~~~~~~~~~~~~~~~~~~ - - ``pytest-asyncio`` integrates with `Hypothesis `_ - to support ``@given`` on async test functions using ``asyncio``. - `#102 `_ - - Pytest 4.1 support. - `#105 `_ - - 0.9.0 (2018-07-28) - ~~~~~~~~~~~~~~~~~~ - - Python 3.7 support. - - Remove ``event_loop_process_pool`` fixture and - ``pytest.mark.asyncio_process_pool`` marker (see - https://bugs.python.org/issue34075 for deprecation and removal details) - - 0.8.0 (2017-09-23) - ~~~~~~~~~~~~~~~~~~ - - Improve integration with other packages (like aiohttp) with more careful event loop handling. - `#64 `_ - - 0.7.0 (2017-09-08) - ~~~~~~~~~~~~~~~~~~ - - Python versions pre-3.6 can use the async_generator library for async fixtures. - `#62 ` - - - 0.6.0 (2017-05-28) - ~~~~~~~~~~~~~~~~~~ - - Support for Python versions pre-3.5 has been dropped. - - ``pytestmark`` now works on both module and class level. - - The ``forbid_global_loop`` parameter has been removed. - - Support for async and async gen fixtures has been added. - `#45 `_ - - The deprecation warning regarding ``asyncio.async()`` has been fixed. - `#51 `_ - - 0.5.0 (2016-09-07) - ~~~~~~~~~~~~~~~~~~ - - Introduced a changelog. - `#31 `_ - - The ``event_loop`` fixture is again responsible for closing itself. - This makes the fixture slightly harder to correctly override, but enables - other fixtures to depend on it correctly. - `#30 `_ - - Deal with the event loop policy by wrapping a special pytest hook, - ``pytest_fixture_setup``. This allows setting the policy before fixtures - dependent on the ``event_loop`` fixture run, thus allowing them to take - advantage of the ``forbid_global_loop`` parameter. As a consequence of this, - we now depend on pytest 3.0. - `#29 `_ - - - 0.4.1 (2016-06-01) - ~~~~~~~~~~~~~~~~~~ - - Fix a bug preventing the propagation of exceptions from the plugin. - `#25 `_ - - 0.4.0 (2016-05-30) - ~~~~~~~~~~~~~~~~~~ - - Make ``event_loop`` fixtures simpler to override by closing them in the - plugin, instead of directly in the fixture. - `#21 `_ - - Introduce the ``forbid_global_loop`` parameter. - `#21 `_ - - 0.3.0 (2015-12-19) - ~~~~~~~~~~~~~~~~~~ - - Support for Python 3.5 ``async``/``await`` syntax. - `#17 `_ - - 0.2.0 (2015-08-01) - ~~~~~~~~~~~~~~~~~~ - - ``unused_tcp_port_factory`` fixture. - `#10 `_ - - - 0.1.1 (2015-04-23) - ~~~~~~~~~~~~~~~~~~ - Initial release. - - - Contributing - ------------ - Contributions are very welcome. Tests can be run with ``tox``, please ensure - the coverage at least stays the same before you submit a pull request. - -Platform: UNKNOWN +Project-URL: GitHub, https://github.com/pytest-dev/pytest-asyncio Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 Classifier: Topic :: Software Development :: Testing +Classifier: Framework :: AsyncIO Classifier: Framework :: Pytest -Requires-Python: >= 3.5 +Classifier: Typing :: Typed +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst Provides-Extra: testing +License-File: LICENSE + +pytest-asyncio: pytest support for asyncio +========================================== + +.. image:: https://img.shields.io/pypi/v/pytest-asyncio.svg + :target: https://pypi.python.org/pypi/pytest-asyncio +.. image:: https://github.com/pytest-dev/pytest-asyncio/workflows/CI/badge.svg + :target: https://github.com/pytest-dev/pytest-asyncio/actions?workflow=CI +.. image:: https://codecov.io/gh/pytest-dev/pytest-asyncio/branch/master/graph/badge.svg + :target: https://codecov.io/gh/pytest-dev/pytest-asyncio +.. image:: https://img.shields.io/pypi/pyversions/pytest-asyncio.svg + :target: https://github.com/pytest-dev/pytest-asyncio + :alt: Supported Python versions +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + +pytest-asyncio is an Apache2 licensed library, written in Python, for testing +asyncio code with pytest. + +asyncio code is usually written in the form of coroutines, which makes it +slightly more difficult to test using normal testing tools. pytest-asyncio +provides useful fixtures and markers to make testing easier. + +.. code-block:: python + + @pytest.mark.asyncio + async def test_some_asyncio_code(): + res = await library.do_something() + assert b"expected result" == res + +pytest-asyncio has been strongly influenced by pytest-tornado_. + +.. _pytest-tornado: https://github.com/eugeniy/pytest-tornado + +Features +-------- + +- fixtures for creating and injecting versions of the asyncio event loop +- fixtures for injecting unused tcp/udp ports +- pytest markers for treating tests as asyncio coroutines +- easy testing with non-default event loops +- support for `async def` fixtures and async generator fixtures +- support *auto* mode to handle all async fixtures and tests automatically by asyncio; + provide *strict* mode if a test suite should work with different async frameworks + simultaneously, e.g. ``asyncio`` and ``trio``. + +Installation +------------ + +To install pytest-asyncio, simply: + +.. code-block:: bash + + $ pip install pytest-asyncio + +This is enough for pytest to pick up pytest-asyncio. + +Modes +----- + +Starting from ``pytest-asyncio>=0.17``, three modes are provided: *auto*, *strict* and +*legacy*. Starting from ``pytest-asyncio>=0.19`` the *strict* mode is the default. + +The mode can be set by ``asyncio_mode`` configuration option in `configuration file +`_: + +.. code-block:: ini + + # pytest.ini + [pytest] + asyncio_mode = auto + +The value can be overridden by command-line option for ``pytest`` invocation: + +.. code-block:: bash + + $ pytest tests --asyncio-mode=strict + +Auto mode +~~~~~~~~~ + +When the mode is auto, all discovered *async* tests are considered *asyncio-driven* even +if they have no ``@pytest.mark.asyncio`` marker. + +All async fixtures are considered *asyncio-driven* as well, even if they are decorated +with a regular ``@pytest.fixture`` decorator instead of dedicated +``@pytest_asyncio.fixture`` counterpart. + +*asyncio-driven* means that tests and fixtures are executed by ``pytest-asyncio`` +plugin. + +This mode requires the simplest tests and fixtures configuration and is +recommended for default usage *unless* the same project and its test suite should +execute tests from different async frameworks, e.g. ``asyncio`` and ``trio``. In this +case, auto-handling can break tests designed for other framework; please use *strict* +mode instead. + +Strict mode +~~~~~~~~~~~ + +Strict mode enforces ``@pytest.mark.asyncio`` and ``@pytest_asyncio.fixture`` usage. +Without these markers, tests and fixtures are not considered as *asyncio-driven*, other +pytest plugin can handle them. + +Please use this mode if multiple async frameworks should be combined in the same test +suite. + +This mode is used by default for the sake of project inter-compatibility. + + +Legacy mode +~~~~~~~~~~~ + +This mode follows rules used by ``pytest-asyncio<0.17``: tests are not auto-marked but +fixtures are. + +Deprecation warnings are emitted with suggestion to either switching to ``auto`` mode +or using ``strict`` mode with ``@pytest_asyncio.fixture`` decorators. + +The default was changed to ``strict`` in ``pytest-asyncio>=0.19``. + + +Fixtures +-------- + +``event_loop`` +~~~~~~~~~~~~~~ +Creates a new asyncio event loop based on the current event loop policy. The new loop +is available as the return value of this fixture or via `asyncio.get_running_loop `__. +The event loop is closed when the fixture scope ends. The fixture scope defaults +to ``function`` scope. + +Note that just using the ``event_loop`` fixture won't make your test function +a coroutine. You'll need to interact with the event loop directly, using methods +like ``event_loop.run_until_complete``. See the ``pytest.mark.asyncio`` marker +for treating test functions like coroutines. + +.. code-block:: python + + def test_http_client(event_loop): + url = "http://httpbin.org/get" + resp = event_loop.run_until_complete(http_client(url)) + assert b"HTTP/1.1 200 OK" in resp + +The ``event_loop`` fixture can be overridden in any of the standard pytest locations, +e.g. directly in the test file, or in ``conftest.py``. This allows redefining the +fixture scope, for example: + +.. code-block:: python + + @pytest.fixture(scope="session") + def event_loop(): + policy = asyncio.get_event_loop_policy() + loop = policy.new_event_loop() + yield loop + loop.close() + +If you need to change the type of the event loop, prefer setting a custom event loop policy over redefining the ``event_loop`` fixture. + +If the ``pytest.mark.asyncio`` marker is applied to a test function, the ``event_loop`` +fixture will be requested automatically by the test function. + +``unused_tcp_port`` +~~~~~~~~~~~~~~~~~~~ +Finds and yields a single unused TCP port on the localhost interface. Useful for +binding temporary test servers. + +``unused_tcp_port_factory`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A callable which returns a different unused TCP port each invocation. Useful +when several unused TCP ports are required in a test. + +.. code-block:: python + + def a_test(unused_tcp_port_factory): + port1, port2 = unused_tcp_port_factory(), unused_tcp_port_factory() + ... + +``unused_udp_port`` and ``unused_udp_port_factory`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Work just like their TCP counterparts but return unused UDP ports. + + +Async fixtures +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Asynchronous fixtures are defined just like ordinary pytest fixtures, except they should be decorated with ``@pytest_asyncio.fixture``. + +.. code-block:: python3 + + import pytest_asyncio + + + @pytest_asyncio.fixture + async def async_gen_fixture(): + await asyncio.sleep(0.1) + yield "a value" + + + @pytest_asyncio.fixture(scope="module") + async def async_fixture(): + return await asyncio.sleep(0.1) + +All scopes are supported, but if you use a non-function scope you will need +to redefine the ``event_loop`` fixture to have the same or broader scope. +Async fixtures need the event loop, and so must have the same or narrower scope +than the ``event_loop`` fixture. + +*auto* and *legacy* mode automatically converts async fixtures declared with the +standard ``@pytest.fixture`` decorator to *asyncio-driven* versions. + + +Markers +------- + +``pytest.mark.asyncio`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Mark your test coroutine with this marker and pytest will execute it as an +asyncio task using the event loop provided by the ``event_loop`` fixture. See +the introductory section for an example. + +The event loop used can be overridden by overriding the ``event_loop`` fixture +(see above). + +In order to make your test code a little more concise, the pytest |pytestmark|_ +feature can be used to mark entire modules or classes with this marker. +Only test coroutines will be affected (by default, coroutines prefixed by +``test_``), so, for example, fixtures are safe to define. + +.. code-block:: python + + import asyncio + + import pytest + + # All test coroutines will be treated as marked. + pytestmark = pytest.mark.asyncio + + + async def test_example(event_loop): + """No marker!""" + await asyncio.sleep(0, loop=event_loop) + +In *auto* mode, the ``pytest.mark.asyncio`` marker can be omitted, the marker is added +automatically to *async* test functions. + + +.. |pytestmark| replace:: ``pytestmark`` +.. _pytestmark: http://doc.pytest.org/en/latest/example/markers.html#marking-whole-classes-or-modules + +Note about unittest +------------------- + +Test classes subclassing the standard `unittest `__ library are not supported, users +are recommended to use `unittest.IsolatedAsyncioTestCase `__ +or an async framework such as `asynctest `__. + +Contributing +------------ +Contributions are very welcome. Tests can be run with ``tox``, please ensure +the coverage at least stays the same before you submit a pull request. diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/README.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/README.rst index 6ea6014cce4..1fc5ef4738e 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/README.rst +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/README.rst @@ -3,13 +3,15 @@ pytest-asyncio: pytest support for asyncio .. image:: https://img.shields.io/pypi/v/pytest-asyncio.svg :target: https://pypi.python.org/pypi/pytest-asyncio -.. image:: https://travis-ci.org/pytest-dev/pytest-asyncio.svg?branch=master - :target: https://travis-ci.org/pytest-dev/pytest-asyncio -.. image:: https://coveralls.io/repos/pytest-dev/pytest-asyncio/badge.svg - :target: https://coveralls.io/r/pytest-dev/pytest-asyncio +.. image:: https://github.com/pytest-dev/pytest-asyncio/workflows/CI/badge.svg + :target: https://github.com/pytest-dev/pytest-asyncio/actions?workflow=CI +.. image:: https://codecov.io/gh/pytest-dev/pytest-asyncio/branch/master/graph/badge.svg + :target: https://codecov.io/gh/pytest-dev/pytest-asyncio .. image:: https://img.shields.io/pypi/pyversions/pytest-asyncio.svg :target: https://github.com/pytest-dev/pytest-asyncio :alt: Supported Python versions +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black pytest-asyncio is an Apache2 licensed library, written in Python, for testing asyncio code with pytest. @@ -23,7 +25,7 @@ provides useful fixtures and markers to make testing easier. @pytest.mark.asyncio async def test_some_asyncio_code(): res = await library.do_something() - assert b'expected result' == res + assert b"expected result" == res pytest-asyncio has been strongly influenced by pytest-tornado_. @@ -33,10 +35,13 @@ Features -------- - fixtures for creating and injecting versions of the asyncio event loop -- fixtures for injecting unused tcp ports +- fixtures for injecting unused tcp/udp ports - pytest markers for treating tests as asyncio coroutines - easy testing with non-default event loops - support for `async def` fixtures and async generator fixtures +- support *auto* mode to handle all async fixtures and tests automatically by asyncio; + provide *strict* mode if a test suite should work with different async frameworks + simultaneously, e.g. ``asyncio`` and ``trio``. Installation ------------ @@ -49,47 +54,110 @@ To install pytest-asyncio, simply: This is enough for pytest to pick up pytest-asyncio. +Modes +----- + +Starting from ``pytest-asyncio>=0.17``, three modes are provided: *auto*, *strict* and +*legacy*. Starting from ``pytest-asyncio>=0.19`` the *strict* mode is the default. + +The mode can be set by ``asyncio_mode`` configuration option in `configuration file +`_: + +.. code-block:: ini + + # pytest.ini + [pytest] + asyncio_mode = auto + +The value can be overridden by command-line option for ``pytest`` invocation: + +.. code-block:: bash + + $ pytest tests --asyncio-mode=strict + +Auto mode +~~~~~~~~~ + +When the mode is auto, all discovered *async* tests are considered *asyncio-driven* even +if they have no ``@pytest.mark.asyncio`` marker. + +All async fixtures are considered *asyncio-driven* as well, even if they are decorated +with a regular ``@pytest.fixture`` decorator instead of dedicated +``@pytest_asyncio.fixture`` counterpart. + +*asyncio-driven* means that tests and fixtures are executed by ``pytest-asyncio`` +plugin. + +This mode requires the simplest tests and fixtures configuration and is +recommended for default usage *unless* the same project and its test suite should +execute tests from different async frameworks, e.g. ``asyncio`` and ``trio``. In this +case, auto-handling can break tests designed for other framework; please use *strict* +mode instead. + +Strict mode +~~~~~~~~~~~ + +Strict mode enforces ``@pytest.mark.asyncio`` and ``@pytest_asyncio.fixture`` usage. +Without these markers, tests and fixtures are not considered as *asyncio-driven*, other +pytest plugin can handle them. + +Please use this mode if multiple async frameworks should be combined in the same test +suite. + +This mode is used by default for the sake of project inter-compatibility. + + +Legacy mode +~~~~~~~~~~~ + +This mode follows rules used by ``pytest-asyncio<0.17``: tests are not auto-marked but +fixtures are. + +Deprecation warnings are emitted with suggestion to either switching to ``auto`` mode +or using ``strict`` mode with ``@pytest_asyncio.fixture`` decorators. + +The default was changed to ``strict`` in ``pytest-asyncio>=0.19``. + + Fixtures -------- ``event_loop`` ~~~~~~~~~~~~~~ -Creates and injects a new instance of the default asyncio event loop. By -default, the loop will be closed at the end of the test (i.e. the default -fixture scope is ``function``). +Creates a new asyncio event loop based on the current event loop policy. The new loop +is available as the return value of this fixture or via `asyncio.get_running_loop `__. +The event loop is closed when the fixture scope ends. The fixture scope defaults +to ``function`` scope. Note that just using the ``event_loop`` fixture won't make your test function a coroutine. You'll need to interact with the event loop directly, using methods like ``event_loop.run_until_complete``. See the ``pytest.mark.asyncio`` marker for treating test functions like coroutines. -Simply using this fixture will not set the generated event loop as the -default asyncio event loop, or change the asyncio event loop policy in any way. -Use ``pytest.mark.asyncio`` for this purpose. - .. code-block:: python def test_http_client(event_loop): - url = 'http://httpbin.org/get' + url = "http://httpbin.org/get" resp = event_loop.run_until_complete(http_client(url)) - assert b'HTTP/1.1 200 OK' in resp + assert b"HTTP/1.1 200 OK" in resp -This fixture can be easily overridden in any of the standard pytest locations -(e.g. directly in the test file, or in ``conftest.py``) to use a non-default -event loop. This will take effect even if you're using the -``pytest.mark.asyncio`` marker and not the ``event_loop`` fixture directly. +The ``event_loop`` fixture can be overridden in any of the standard pytest locations, +e.g. directly in the test file, or in ``conftest.py``. This allows redefining the +fixture scope, for example: .. code-block:: python - @pytest.fixture + @pytest.fixture(scope="session") def event_loop(): - loop = MyCustomLoop() + policy = asyncio.get_event_loop_policy() + loop = policy.new_event_loop() yield loop loop.close() -If the ``pytest.mark.asyncio`` marker is applied, a pytest hook will -ensure the produced loop is set as the default global loop. -Fixtures depending on the ``event_loop`` fixture can expect the policy to be properly modified when they run. +If you need to change the type of the event loop, prefer setting a custom event loop policy over redefining the ``event_loop`` fixture. + +If the ``pytest.mark.asyncio`` marker is applied to a test function, the ``event_loop`` +fixture will be requested automatically by the test function. ``unused_tcp_port`` ~~~~~~~~~~~~~~~~~~~ @@ -107,18 +175,27 @@ when several unused TCP ports are required in a test. port1, port2 = unused_tcp_port_factory(), unused_tcp_port_factory() ... +``unused_udp_port`` and ``unused_udp_port_factory`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Work just like their TCP counterparts but return unused UDP ports. + + Async fixtures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Asynchronous fixtures are defined just like ordinary pytest fixtures, except they should be coroutines or asynchronous generators. +Asynchronous fixtures are defined just like ordinary pytest fixtures, except they should be decorated with ``@pytest_asyncio.fixture``. .. code-block:: python3 - @pytest.fixture + import pytest_asyncio + + + @pytest_asyncio.fixture async def async_gen_fixture(): await asyncio.sleep(0.1) - yield 'a value' + yield "a value" - @pytest.fixture(scope='module') + + @pytest_asyncio.fixture(scope="module") async def async_fixture(): return await asyncio.sleep(0.1) @@ -127,18 +204,8 @@ to redefine the ``event_loop`` fixture to have the same or broader scope. Async fixtures need the event loop, and so must have the same or narrower scope than the ``event_loop`` fixture. -If you want to do this with Python 3.5, the ``yield`` statement must be replaced with ``await yield_()`` and the coroutine -function must be decorated with ``@async_generator``, like so: - -.. code-block:: python3 - - from async_generator import yield_, async_generator - - @pytest.fixture - @async_generator - async def async_gen_fixture(): - await asyncio.sleep(0.1) - await yield_('a value') +*auto* and *legacy* mode automatically converts async fixtures declared with the +standard ``@pytest.fixture`` decorator to *asyncio-driven* versions. Markers @@ -150,7 +217,7 @@ Mark your test coroutine with this marker and pytest will execute it as an asyncio task using the event loop provided by the ``event_loop`` fixture. See the introductory section for an example. -The event loop used can be overriden by overriding the ``event_loop`` fixture +The event loop used can be overridden by overriding the ``event_loop`` fixture (see above). In order to make your test code a little more concise, the pytest |pytestmark|_ @@ -161,119 +228,30 @@ Only test coroutines will be affected (by default, coroutines prefixed by .. code-block:: python import asyncio + import pytest # All test coroutines will be treated as marked. pytestmark = pytest.mark.asyncio + async def test_example(event_loop): """No marker!""" await asyncio.sleep(0, loop=event_loop) +In *auto* mode, the ``pytest.mark.asyncio`` marker can be omitted, the marker is added +automatically to *async* test functions. + + .. |pytestmark| replace:: ``pytestmark`` .. _pytestmark: http://doc.pytest.org/en/latest/example/markers.html#marking-whole-classes-or-modules -Changelog ---------- -0.13.0 (2020-06-24) -~~~~~~~~~~~~~~~~~~~ -- Fix `#162 `_, and ``event_loop`` fixture behavior now is coherent on all scopes. - `#164 `_ - -0.12.0 (2020-05-04) -~~~~~~~~~~~~~~~~~~~ -- Run the event loop fixture as soon as possible. This helps with fixtures that have an implicit dependency on the event loop. - `#156 `_ - -0.11.0 (2020-04-20) -~~~~~~~~~~~~~~~~~~~ -- Test on 3.8, drop 3.3 and 3.4. Stick to 0.10 for these versions. - `#152 `_ -- Use the new Pytest 5.4.0 Function API. We therefore depend on pytest >= 5.4.0. - `#142 `_ -- Better ``pytest.skip`` support. - `#126 `_ - -0.10.0 (2019-01-08) -~~~~~~~~~~~~~~~~~~~~ -- ``pytest-asyncio`` integrates with `Hypothesis `_ - to support ``@given`` on async test functions using ``asyncio``. - `#102 `_ -- Pytest 4.1 support. - `#105 `_ - -0.9.0 (2018-07-28) -~~~~~~~~~~~~~~~~~~ -- Python 3.7 support. -- Remove ``event_loop_process_pool`` fixture and - ``pytest.mark.asyncio_process_pool`` marker (see - https://bugs.python.org/issue34075 for deprecation and removal details) - -0.8.0 (2017-09-23) -~~~~~~~~~~~~~~~~~~ -- Improve integration with other packages (like aiohttp) with more careful event loop handling. - `#64 `_ - -0.7.0 (2017-09-08) -~~~~~~~~~~~~~~~~~~ -- Python versions pre-3.6 can use the async_generator library for async fixtures. - `#62 ` - - -0.6.0 (2017-05-28) -~~~~~~~~~~~~~~~~~~ -- Support for Python versions pre-3.5 has been dropped. -- ``pytestmark`` now works on both module and class level. -- The ``forbid_global_loop`` parameter has been removed. -- Support for async and async gen fixtures has been added. - `#45 `_ -- The deprecation warning regarding ``asyncio.async()`` has been fixed. - `#51 `_ - -0.5.0 (2016-09-07) -~~~~~~~~~~~~~~~~~~ -- Introduced a changelog. - `#31 `_ -- The ``event_loop`` fixture is again responsible for closing itself. - This makes the fixture slightly harder to correctly override, but enables - other fixtures to depend on it correctly. - `#30 `_ -- Deal with the event loop policy by wrapping a special pytest hook, - ``pytest_fixture_setup``. This allows setting the policy before fixtures - dependent on the ``event_loop`` fixture run, thus allowing them to take - advantage of the ``forbid_global_loop`` parameter. As a consequence of this, - we now depend on pytest 3.0. - `#29 `_ - - -0.4.1 (2016-06-01) -~~~~~~~~~~~~~~~~~~ -- Fix a bug preventing the propagation of exceptions from the plugin. - `#25 `_ - -0.4.0 (2016-05-30) -~~~~~~~~~~~~~~~~~~ -- Make ``event_loop`` fixtures simpler to override by closing them in the - plugin, instead of directly in the fixture. - `#21 `_ -- Introduce the ``forbid_global_loop`` parameter. - `#21 `_ - -0.3.0 (2015-12-19) -~~~~~~~~~~~~~~~~~~ -- Support for Python 3.5 ``async``/``await`` syntax. - `#17 `_ - -0.2.0 (2015-08-01) -~~~~~~~~~~~~~~~~~~ -- ``unused_tcp_port_factory`` fixture. - `#10 `_ - - -0.1.1 (2015-04-23) -~~~~~~~~~~~~~~~~~~ -Initial release. +Note about unittest +------------------- +Test classes subclassing the standard `unittest `__ library are not supported, users +are recommended to use `unittest.IsolatedAsyncioTestCase `__ +or an async framework such as `asynctest `__. Contributing ------------ diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/default/constraints.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/default/constraints.txt new file mode 100644 index 00000000000..cd99339f5e0 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/default/constraints.txt @@ -0,0 +1,24 @@ +async-generator==1.10 +attrs==21.4.0 +coverage==6.4.1 +flaky==3.7.0 +hypothesis==6.48.3 +idna==3.3 +importlib-metadata==4.12.0 +iniconfig==1.1.1 +mypy==0.961 +mypy-extensions==0.4.3 +outcome==1.2.0 +packaging==21.3 +pluggy==1.0.0 +py==1.11.0 +pyparsing==3.0.9 +pytest==7.1.2 +pytest-trio==0.7.0 +sniffio==1.2.0 +sortedcontainers==2.4.0 +tomli==2.0.1 +trio==0.21.0 +typed-ast==1.5.4 +typing_extensions==4.3.0 +zipp==3.8.0 diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/default/requirements.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/default/requirements.txt new file mode 100644 index 00000000000..01b2484e6b2 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/default/requirements.txt @@ -0,0 +1,4 @@ +# Always adjust install_requires in setup.cfg and pytest-min-requirements.txt +# when changing runtime dependencies +pytest >= 6.1.0 +typing-extensions >= 3.7.2; python_version < "3.8" diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/pytest-min/constraints.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/pytest-min/constraints.txt new file mode 100644 index 00000000000..33f7948f4c7 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/pytest-min/constraints.txt @@ -0,0 +1,22 @@ +async-generator==1.10 +attrs==21.4.0 +coverage==6.3.2 +flaky==3.7.0 +hypothesis==6.43.3 +idna==3.3 +iniconfig==1.1.1 +mypy==0.942 +mypy-extensions==0.4.3 +outcome==1.1.0 +packaging==21.3 +pluggy==0.13.1 +py==1.11.0 +pyparsing==3.0.8 +pytest==6.1.0 +pytest-trio==0.7.0 +sniffio==1.2.0 +sortedcontainers==2.4.0 +toml==0.10.2 +tomli==2.0.1 +trio==0.20.0 +typing_extensions==4.2.0 diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/pytest-min/requirements.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/pytest-min/requirements.txt new file mode 100644 index 00000000000..4fc6ef2fa32 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/dependencies/pytest-min/requirements.txt @@ -0,0 +1,4 @@ +# Always adjust install_requires in setup.cfg and requirements.txt +# when changing minimum version dependencies +pytest == 6.1.0 +typing-extensions >= 3.7.2; python_version < "3.8" diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pyproject.toml b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pyproject.toml new file mode 100644 index 00000000000..81540a53a67 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pyproject.toml @@ -0,0 +1,10 @@ +[build-system] +requires = [ + "setuptools>=51.0", + "wheel>=0.36", + "setuptools_scm[toml]>=6.2" +] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +write_to = "pytest_asyncio/_version.py" diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/PKG-INFO b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/PKG-INFO index bb7611bc1a7..19acaa4d513 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/PKG-INFO +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/PKG-INFO @@ -1,302 +1,285 @@ Metadata-Version: 2.1 Name: pytest-asyncio -Version: 0.14.0 -Summary: Pytest support for asyncio. +Version: 0.19.0 +Summary: Pytest support for asyncio Home-page: https://github.com/pytest-dev/pytest-asyncio -Author: Tin Tvrtković +Author: Tin Tvrtković Author-email: tinchester@gmail.com License: Apache 2.0 -Description: pytest-asyncio: pytest support for asyncio - ========================================== - - .. image:: https://img.shields.io/pypi/v/pytest-asyncio.svg - :target: https://pypi.python.org/pypi/pytest-asyncio - .. image:: https://travis-ci.org/pytest-dev/pytest-asyncio.svg?branch=master - :target: https://travis-ci.org/pytest-dev/pytest-asyncio - .. image:: https://coveralls.io/repos/pytest-dev/pytest-asyncio/badge.svg - :target: https://coveralls.io/r/pytest-dev/pytest-asyncio - .. image:: https://img.shields.io/pypi/pyversions/pytest-asyncio.svg - :target: https://github.com/pytest-dev/pytest-asyncio - :alt: Supported Python versions - - pytest-asyncio is an Apache2 licensed library, written in Python, for testing - asyncio code with pytest. - - asyncio code is usually written in the form of coroutines, which makes it - slightly more difficult to test using normal testing tools. pytest-asyncio - provides useful fixtures and markers to make testing easier. - - .. code-block:: python - - @pytest.mark.asyncio - async def test_some_asyncio_code(): - res = await library.do_something() - assert b'expected result' == res - - pytest-asyncio has been strongly influenced by pytest-tornado_. - - .. _pytest-tornado: https://github.com/eugeniy/pytest-tornado - - Features - -------- - - - fixtures for creating and injecting versions of the asyncio event loop - - fixtures for injecting unused tcp ports - - pytest markers for treating tests as asyncio coroutines - - easy testing with non-default event loops - - support for `async def` fixtures and async generator fixtures - - Installation - ------------ - - To install pytest-asyncio, simply: - - .. code-block:: bash - - $ pip install pytest-asyncio - - This is enough for pytest to pick up pytest-asyncio. - - Fixtures - -------- - - ``event_loop`` - ~~~~~~~~~~~~~~ - Creates and injects a new instance of the default asyncio event loop. By - default, the loop will be closed at the end of the test (i.e. the default - fixture scope is ``function``). - - Note that just using the ``event_loop`` fixture won't make your test function - a coroutine. You'll need to interact with the event loop directly, using methods - like ``event_loop.run_until_complete``. See the ``pytest.mark.asyncio`` marker - for treating test functions like coroutines. - - Simply using this fixture will not set the generated event loop as the - default asyncio event loop, or change the asyncio event loop policy in any way. - Use ``pytest.mark.asyncio`` for this purpose. - - .. code-block:: python - - def test_http_client(event_loop): - url = 'http://httpbin.org/get' - resp = event_loop.run_until_complete(http_client(url)) - assert b'HTTP/1.1 200 OK' in resp - - This fixture can be easily overridden in any of the standard pytest locations - (e.g. directly in the test file, or in ``conftest.py``) to use a non-default - event loop. This will take effect even if you're using the - ``pytest.mark.asyncio`` marker and not the ``event_loop`` fixture directly. - - .. code-block:: python - - @pytest.fixture - def event_loop(): - loop = MyCustomLoop() - yield loop - loop.close() - - If the ``pytest.mark.asyncio`` marker is applied, a pytest hook will - ensure the produced loop is set as the default global loop. - Fixtures depending on the ``event_loop`` fixture can expect the policy to be properly modified when they run. - - ``unused_tcp_port`` - ~~~~~~~~~~~~~~~~~~~ - Finds and yields a single unused TCP port on the localhost interface. Useful for - binding temporary test servers. - - ``unused_tcp_port_factory`` - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - A callable which returns a different unused TCP port each invocation. Useful - when several unused TCP ports are required in a test. - - .. code-block:: python - - def a_test(unused_tcp_port_factory): - port1, port2 = unused_tcp_port_factory(), unused_tcp_port_factory() - ... - - Async fixtures - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Asynchronous fixtures are defined just like ordinary pytest fixtures, except they should be coroutines or asynchronous generators. - - .. code-block:: python3 - - @pytest.fixture - async def async_gen_fixture(): - await asyncio.sleep(0.1) - yield 'a value' - - @pytest.fixture(scope='module') - async def async_fixture(): - return await asyncio.sleep(0.1) - - All scopes are supported, but if you use a non-function scope you will need - to redefine the ``event_loop`` fixture to have the same or broader scope. - Async fixtures need the event loop, and so must have the same or narrower scope - than the ``event_loop`` fixture. - - If you want to do this with Python 3.5, the ``yield`` statement must be replaced with ``await yield_()`` and the coroutine - function must be decorated with ``@async_generator``, like so: - - .. code-block:: python3 - - from async_generator import yield_, async_generator - - @pytest.fixture - @async_generator - async def async_gen_fixture(): - await asyncio.sleep(0.1) - await yield_('a value') - - - Markers - ------- - - ``pytest.mark.asyncio`` - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Mark your test coroutine with this marker and pytest will execute it as an - asyncio task using the event loop provided by the ``event_loop`` fixture. See - the introductory section for an example. - - The event loop used can be overriden by overriding the ``event_loop`` fixture - (see above). - - In order to make your test code a little more concise, the pytest |pytestmark|_ - feature can be used to mark entire modules or classes with this marker. - Only test coroutines will be affected (by default, coroutines prefixed by - ``test_``), so, for example, fixtures are safe to define. - - .. code-block:: python - - import asyncio - import pytest - - # All test coroutines will be treated as marked. - pytestmark = pytest.mark.asyncio - - async def test_example(event_loop): - """No marker!""" - await asyncio.sleep(0, loop=event_loop) - - .. |pytestmark| replace:: ``pytestmark`` - .. _pytestmark: http://doc.pytest.org/en/latest/example/markers.html#marking-whole-classes-or-modules - - Changelog - --------- - 0.13.0 (2020-06-24) - ~~~~~~~~~~~~~~~~~~~ - - Fix `#162 `_, and ``event_loop`` fixture behavior now is coherent on all scopes. - `#164 `_ - - 0.12.0 (2020-05-04) - ~~~~~~~~~~~~~~~~~~~ - - Run the event loop fixture as soon as possible. This helps with fixtures that have an implicit dependency on the event loop. - `#156 `_ - - 0.11.0 (2020-04-20) - ~~~~~~~~~~~~~~~~~~~ - - Test on 3.8, drop 3.3 and 3.4. Stick to 0.10 for these versions. - `#152 `_ - - Use the new Pytest 5.4.0 Function API. We therefore depend on pytest >= 5.4.0. - `#142 `_ - - Better ``pytest.skip`` support. - `#126 `_ - - 0.10.0 (2019-01-08) - ~~~~~~~~~~~~~~~~~~~~ - - ``pytest-asyncio`` integrates with `Hypothesis `_ - to support ``@given`` on async test functions using ``asyncio``. - `#102 `_ - - Pytest 4.1 support. - `#105 `_ - - 0.9.0 (2018-07-28) - ~~~~~~~~~~~~~~~~~~ - - Python 3.7 support. - - Remove ``event_loop_process_pool`` fixture and - ``pytest.mark.asyncio_process_pool`` marker (see - https://bugs.python.org/issue34075 for deprecation and removal details) - - 0.8.0 (2017-09-23) - ~~~~~~~~~~~~~~~~~~ - - Improve integration with other packages (like aiohttp) with more careful event loop handling. - `#64 `_ - - 0.7.0 (2017-09-08) - ~~~~~~~~~~~~~~~~~~ - - Python versions pre-3.6 can use the async_generator library for async fixtures. - `#62 ` - - - 0.6.0 (2017-05-28) - ~~~~~~~~~~~~~~~~~~ - - Support for Python versions pre-3.5 has been dropped. - - ``pytestmark`` now works on both module and class level. - - The ``forbid_global_loop`` parameter has been removed. - - Support for async and async gen fixtures has been added. - `#45 `_ - - The deprecation warning regarding ``asyncio.async()`` has been fixed. - `#51 `_ - - 0.5.0 (2016-09-07) - ~~~~~~~~~~~~~~~~~~ - - Introduced a changelog. - `#31 `_ - - The ``event_loop`` fixture is again responsible for closing itself. - This makes the fixture slightly harder to correctly override, but enables - other fixtures to depend on it correctly. - `#30 `_ - - Deal with the event loop policy by wrapping a special pytest hook, - ``pytest_fixture_setup``. This allows setting the policy before fixtures - dependent on the ``event_loop`` fixture run, thus allowing them to take - advantage of the ``forbid_global_loop`` parameter. As a consequence of this, - we now depend on pytest 3.0. - `#29 `_ - - - 0.4.1 (2016-06-01) - ~~~~~~~~~~~~~~~~~~ - - Fix a bug preventing the propagation of exceptions from the plugin. - `#25 `_ - - 0.4.0 (2016-05-30) - ~~~~~~~~~~~~~~~~~~ - - Make ``event_loop`` fixtures simpler to override by closing them in the - plugin, instead of directly in the fixture. - `#21 `_ - - Introduce the ``forbid_global_loop`` parameter. - `#21 `_ - - 0.3.0 (2015-12-19) - ~~~~~~~~~~~~~~~~~~ - - Support for Python 3.5 ``async``/``await`` syntax. - `#17 `_ - - 0.2.0 (2015-08-01) - ~~~~~~~~~~~~~~~~~~ - - ``unused_tcp_port_factory`` fixture. - `#10 `_ - - - 0.1.1 (2015-04-23) - ~~~~~~~~~~~~~~~~~~ - Initial release. - - - Contributing - ------------ - Contributions are very welcome. Tests can be run with ``tox``, please ensure - the coverage at least stays the same before you submit a pull request. - -Platform: UNKNOWN +Project-URL: GitHub, https://github.com/pytest-dev/pytest-asyncio Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 Classifier: Topic :: Software Development :: Testing +Classifier: Framework :: AsyncIO Classifier: Framework :: Pytest -Requires-Python: >= 3.5 +Classifier: Typing :: Typed +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst Provides-Extra: testing +License-File: LICENSE + +pytest-asyncio: pytest support for asyncio +========================================== + +.. image:: https://img.shields.io/pypi/v/pytest-asyncio.svg + :target: https://pypi.python.org/pypi/pytest-asyncio +.. image:: https://github.com/pytest-dev/pytest-asyncio/workflows/CI/badge.svg + :target: https://github.com/pytest-dev/pytest-asyncio/actions?workflow=CI +.. image:: https://codecov.io/gh/pytest-dev/pytest-asyncio/branch/master/graph/badge.svg + :target: https://codecov.io/gh/pytest-dev/pytest-asyncio +.. image:: https://img.shields.io/pypi/pyversions/pytest-asyncio.svg + :target: https://github.com/pytest-dev/pytest-asyncio + :alt: Supported Python versions +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + +pytest-asyncio is an Apache2 licensed library, written in Python, for testing +asyncio code with pytest. + +asyncio code is usually written in the form of coroutines, which makes it +slightly more difficult to test using normal testing tools. pytest-asyncio +provides useful fixtures and markers to make testing easier. + +.. code-block:: python + + @pytest.mark.asyncio + async def test_some_asyncio_code(): + res = await library.do_something() + assert b"expected result" == res + +pytest-asyncio has been strongly influenced by pytest-tornado_. + +.. _pytest-tornado: https://github.com/eugeniy/pytest-tornado + +Features +-------- + +- fixtures for creating and injecting versions of the asyncio event loop +- fixtures for injecting unused tcp/udp ports +- pytest markers for treating tests as asyncio coroutines +- easy testing with non-default event loops +- support for `async def` fixtures and async generator fixtures +- support *auto* mode to handle all async fixtures and tests automatically by asyncio; + provide *strict* mode if a test suite should work with different async frameworks + simultaneously, e.g. ``asyncio`` and ``trio``. + +Installation +------------ + +To install pytest-asyncio, simply: + +.. code-block:: bash + + $ pip install pytest-asyncio + +This is enough for pytest to pick up pytest-asyncio. + +Modes +----- + +Starting from ``pytest-asyncio>=0.17``, three modes are provided: *auto*, *strict* and +*legacy*. Starting from ``pytest-asyncio>=0.19`` the *strict* mode is the default. + +The mode can be set by ``asyncio_mode`` configuration option in `configuration file +`_: + +.. code-block:: ini + + # pytest.ini + [pytest] + asyncio_mode = auto + +The value can be overridden by command-line option for ``pytest`` invocation: + +.. code-block:: bash + + $ pytest tests --asyncio-mode=strict + +Auto mode +~~~~~~~~~ + +When the mode is auto, all discovered *async* tests are considered *asyncio-driven* even +if they have no ``@pytest.mark.asyncio`` marker. + +All async fixtures are considered *asyncio-driven* as well, even if they are decorated +with a regular ``@pytest.fixture`` decorator instead of dedicated +``@pytest_asyncio.fixture`` counterpart. + +*asyncio-driven* means that tests and fixtures are executed by ``pytest-asyncio`` +plugin. + +This mode requires the simplest tests and fixtures configuration and is +recommended for default usage *unless* the same project and its test suite should +execute tests from different async frameworks, e.g. ``asyncio`` and ``trio``. In this +case, auto-handling can break tests designed for other framework; please use *strict* +mode instead. + +Strict mode +~~~~~~~~~~~ + +Strict mode enforces ``@pytest.mark.asyncio`` and ``@pytest_asyncio.fixture`` usage. +Without these markers, tests and fixtures are not considered as *asyncio-driven*, other +pytest plugin can handle them. + +Please use this mode if multiple async frameworks should be combined in the same test +suite. + +This mode is used by default for the sake of project inter-compatibility. + + +Legacy mode +~~~~~~~~~~~ + +This mode follows rules used by ``pytest-asyncio<0.17``: tests are not auto-marked but +fixtures are. + +Deprecation warnings are emitted with suggestion to either switching to ``auto`` mode +or using ``strict`` mode with ``@pytest_asyncio.fixture`` decorators. + +The default was changed to ``strict`` in ``pytest-asyncio>=0.19``. + + +Fixtures +-------- + +``event_loop`` +~~~~~~~~~~~~~~ +Creates a new asyncio event loop based on the current event loop policy. The new loop +is available as the return value of this fixture or via `asyncio.get_running_loop `__. +The event loop is closed when the fixture scope ends. The fixture scope defaults +to ``function`` scope. + +Note that just using the ``event_loop`` fixture won't make your test function +a coroutine. You'll need to interact with the event loop directly, using methods +like ``event_loop.run_until_complete``. See the ``pytest.mark.asyncio`` marker +for treating test functions like coroutines. + +.. code-block:: python + + def test_http_client(event_loop): + url = "http://httpbin.org/get" + resp = event_loop.run_until_complete(http_client(url)) + assert b"HTTP/1.1 200 OK" in resp + +The ``event_loop`` fixture can be overridden in any of the standard pytest locations, +e.g. directly in the test file, or in ``conftest.py``. This allows redefining the +fixture scope, for example: + +.. code-block:: python + + @pytest.fixture(scope="session") + def event_loop(): + policy = asyncio.get_event_loop_policy() + loop = policy.new_event_loop() + yield loop + loop.close() + +If you need to change the type of the event loop, prefer setting a custom event loop policy over redefining the ``event_loop`` fixture. + +If the ``pytest.mark.asyncio`` marker is applied to a test function, the ``event_loop`` +fixture will be requested automatically by the test function. + +``unused_tcp_port`` +~~~~~~~~~~~~~~~~~~~ +Finds and yields a single unused TCP port on the localhost interface. Useful for +binding temporary test servers. + +``unused_tcp_port_factory`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A callable which returns a different unused TCP port each invocation. Useful +when several unused TCP ports are required in a test. + +.. code-block:: python + + def a_test(unused_tcp_port_factory): + port1, port2 = unused_tcp_port_factory(), unused_tcp_port_factory() + ... + +``unused_udp_port`` and ``unused_udp_port_factory`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Work just like their TCP counterparts but return unused UDP ports. + + +Async fixtures +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Asynchronous fixtures are defined just like ordinary pytest fixtures, except they should be decorated with ``@pytest_asyncio.fixture``. + +.. code-block:: python3 + + import pytest_asyncio + + + @pytest_asyncio.fixture + async def async_gen_fixture(): + await asyncio.sleep(0.1) + yield "a value" + + + @pytest_asyncio.fixture(scope="module") + async def async_fixture(): + return await asyncio.sleep(0.1) + +All scopes are supported, but if you use a non-function scope you will need +to redefine the ``event_loop`` fixture to have the same or broader scope. +Async fixtures need the event loop, and so must have the same or narrower scope +than the ``event_loop`` fixture. + +*auto* and *legacy* mode automatically converts async fixtures declared with the +standard ``@pytest.fixture`` decorator to *asyncio-driven* versions. + + +Markers +------- + +``pytest.mark.asyncio`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Mark your test coroutine with this marker and pytest will execute it as an +asyncio task using the event loop provided by the ``event_loop`` fixture. See +the introductory section for an example. + +The event loop used can be overridden by overriding the ``event_loop`` fixture +(see above). + +In order to make your test code a little more concise, the pytest |pytestmark|_ +feature can be used to mark entire modules or classes with this marker. +Only test coroutines will be affected (by default, coroutines prefixed by +``test_``), so, for example, fixtures are safe to define. + +.. code-block:: python + + import asyncio + + import pytest + + # All test coroutines will be treated as marked. + pytestmark = pytest.mark.asyncio + + + async def test_example(event_loop): + """No marker!""" + await asyncio.sleep(0, loop=event_loop) + +In *auto* mode, the ``pytest.mark.asyncio`` marker can be omitted, the marker is added +automatically to *async* test functions. + + +.. |pytestmark| replace:: ``pytestmark`` +.. _pytestmark: http://doc.pytest.org/en/latest/example/markers.html#marking-whole-classes-or-modules + +Note about unittest +------------------- + +Test classes subclassing the standard `unittest `__ library are not supported, users +are recommended to use `unittest.IsolatedAsyncioTestCase `__ +or an async framework such as `asynctest `__. + +Contributing +------------ +Contributions are very welcome. Tests can be run with ``tox``, please ensure +the coverage at least stays the same before you submit a pull request. diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/SOURCES.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/SOURCES.txt index 40cd5d7c7fc..a016c1cae04 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/SOURCES.txt +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/SOURCES.txt @@ -1,12 +1,51 @@ +CHANGELOG.rst LICENSE +MANIFEST.in +Makefile README.rst +pyproject.toml setup.cfg -setup.py +tox.ini +dependencies/default/constraints.txt +dependencies/default/requirements.txt +dependencies/pytest-min/constraints.txt +dependencies/pytest-min/requirements.txt pytest_asyncio/__init__.py +pytest_asyncio/_version.py pytest_asyncio/plugin.py +pytest_asyncio/py.typed pytest_asyncio.egg-info/PKG-INFO pytest_asyncio.egg-info/SOURCES.txt pytest_asyncio.egg-info/dependency_links.txt pytest_asyncio.egg-info/entry_points.txt pytest_asyncio.egg-info/requires.txt -pytest_asyncio.egg-info/top_level.txt \ No newline at end of file +pytest_asyncio.egg-info/top_level.txt +tests/conftest.py +tests/test_asyncio_fixture.py +tests/test_dependent_fixtures.py +tests/test_event_loop_scope.py +tests/test_flaky_integration.py +tests/test_simple.py +tests/test_subprocess.py +tests/async_fixtures/__init__.py +tests/async_fixtures/test_async_fixtures.py +tests/async_fixtures/test_async_fixtures_scope.py +tests/async_fixtures/test_async_fixtures_with_finalizer.py +tests/async_fixtures/test_async_gen_fixtures.py +tests/async_fixtures/test_nested.py +tests/async_fixtures/test_parametrized_loop.py +tests/hypothesis/test_base.py +tests/hypothesis/test_inherited_test.py +tests/loop_fixture_scope/conftest.py +tests/loop_fixture_scope/test_loop_fixture_scope.py +tests/markers/test_class_marker.py +tests/markers/test_module_marker.py +tests/modes/test_auto_mode.py +tests/modes/test_legacy_mode.py +tests/modes/test_strict_mode.py +tests/multiloop/conftest.py +tests/multiloop/test_alternative_loops.py +tests/respect_event_loop_policy/conftest.py +tests/respect_event_loop_policy/test_respects_event_loop_policy.py +tests/trio/test_fixtures.py +tools/get-version.py \ No newline at end of file diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/entry_points.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/entry_points.txt index 69c53be38a8..88db714dad5 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/entry_points.txt +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/entry_points.txt @@ -1,3 +1,2 @@ [pytest11] asyncio = pytest_asyncio.plugin - diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/requires.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/requires.txt index d546f122ad1..1e3119384dc 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/requires.txt +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio.egg-info/requires.txt @@ -1,9 +1,11 @@ -pytest>=5.4.0 +pytest>=6.1.0 -[:python_version == "3.5"] -async_generator>=1.3 +[:python_version < "3.8"] +typing-extensions>=3.7.2 [testing] -async_generator>=1.3 -coverage +coverage>=6.2 hypothesis>=5.7.1 +flaky>=3.5.0 +mypy>=0.931 +pytest-trio>=0.7.0 diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/__init__.py index 61c5f43b959..1bc2811d93f 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/__init__.py +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/__init__.py @@ -1,2 +1,5 @@ """The main point for importing pytest-asyncio items.""" -__version__ = "0.14.0" +from ._version import version as __version__ # noqa +from .plugin import fixture + +__all__ = ("fixture",) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/_version.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/_version.py new file mode 100644 index 00000000000..76aa7a209ac --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/_version.py @@ -0,0 +1,5 @@ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +__version__ = version = '0.19.0' +__version_tuple__ = version_tuple = (0, 19, 0) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/plugin.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/plugin.py index 2fdc5f4e774..dd6a782b4d3 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/plugin.py +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/plugin.py @@ -1,240 +1,546 @@ """pytest-asyncio implementation.""" import asyncio import contextlib +import enum import functools import inspect import socket +import sys +import warnings +from typing import ( + Any, + AsyncIterator, + Awaitable, + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Set, + TypeVar, + Union, + cast, + overload, +) import pytest -try: - from _pytest.python import transfer_markers -except ImportError: # Pytest 4.1.0 removes the transfer_marker api (#104) - def transfer_markers(*args, **kwargs): # noqa - """Noop when over pytest 4.1.0""" - pass -try: - from async_generator import isasyncgenfunction -except ImportError: - from inspect import isasyncgenfunction +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + +_R = TypeVar("_R") + +_ScopeName = Literal["session", "package", "module", "class", "function"] +_T = TypeVar("_T") + +SimpleFixtureFunction = TypeVar( + "SimpleFixtureFunction", bound=Callable[..., Awaitable[_R]] +) +FactoryFixtureFunction = TypeVar( + "FactoryFixtureFunction", bound=Callable[..., AsyncIterator[_R]] +) +FixtureFunction = Union[SimpleFixtureFunction, FactoryFixtureFunction] +FixtureFunctionMarker = Callable[[FixtureFunction], FixtureFunction] + +Config = Any # pytest < 7.0 +PytestPluginManager = Any # pytest < 7.0 +FixtureDef = Any # pytest < 7.0 +Parser = Any # pytest < 7.0 +SubRequest = Any # pytest < 7.0 -def _is_coroutine(obj): +class Mode(str, enum.Enum): + AUTO = "auto" + STRICT = "strict" + LEGACY = "legacy" + + +LEGACY_MODE = DeprecationWarning( + "The 'asyncio_mode' default value will change to 'strict' in future, " + "please explicitly use 'asyncio_mode=strict' or 'asyncio_mode=auto' " + "in pytest configuration file." +) + +LEGACY_ASYNCIO_FIXTURE = ( + "'@pytest.fixture' is applied to {name} " + "in 'legacy' mode, " + "please replace it with '@pytest_asyncio.fixture' as a preparation " + "for switching to 'strict' mode (or use 'auto' mode to seamlessly handle " + "all these fixtures as asyncio-driven)." +) + + +ASYNCIO_MODE_HELP = """\ +'auto' - for automatically handling all async functions by the plugin +'strict' - for autoprocessing disabling (useful if different async frameworks \ +should be tested together, e.g. \ +both pytest-asyncio and pytest-trio are used in the same project) +'legacy' - for keeping compatibility with pytest-asyncio<0.17: \ +auto-handling is disabled but pytest_asyncio.fixture usage is not enforced +""" + + +def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None: + group = parser.getgroup("asyncio") + group.addoption( + "--asyncio-mode", + dest="asyncio_mode", + default=None, + metavar="MODE", + help=ASYNCIO_MODE_HELP, + ) + parser.addini( + "asyncio_mode", + help="default value for --asyncio-mode", + default="strict", + ) + + +@overload +def fixture( + fixture_function: FixtureFunction, + *, + scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ..., + params: Optional[Iterable[object]] = ..., + autouse: bool = ..., + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = ..., + name: Optional[str] = ..., +) -> FixtureFunction: + ... + + +@overload +def fixture( + fixture_function: None = ..., + *, + scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ..., + params: Optional[Iterable[object]] = ..., + autouse: bool = ..., + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = ..., + name: Optional[str] = None, +) -> FixtureFunctionMarker: + ... + + +def fixture( + fixture_function: Optional[FixtureFunction] = None, **kwargs: Any +) -> Union[FixtureFunction, FixtureFunctionMarker]: + if fixture_function is not None: + _set_explicit_asyncio_mark(fixture_function) + return pytest.fixture(fixture_function, **kwargs) + + else: + + @functools.wraps(fixture) + def inner(fixture_function: FixtureFunction) -> FixtureFunction: + return fixture(fixture_function, **kwargs) + + return inner + + +def _has_explicit_asyncio_mark(obj: Any) -> bool: + obj = getattr(obj, "__func__", obj) # instance method maybe? + return getattr(obj, "_force_asyncio_fixture", False) + + +def _set_explicit_asyncio_mark(obj: Any) -> None: + if hasattr(obj, "__func__"): + # instance method, check the function object + obj = obj.__func__ + obj._force_asyncio_fixture = True + + +def _is_coroutine(obj: Any) -> bool: """Check to see if an object is really an asyncio coroutine.""" - return asyncio.iscoroutinefunction(obj) or inspect.isgeneratorfunction(obj) + return asyncio.iscoroutinefunction(obj) -def pytest_configure(config): +def _is_coroutine_or_asyncgen(obj: Any) -> bool: + return _is_coroutine(obj) or inspect.isasyncgenfunction(obj) + + +def _get_asyncio_mode(config: Config) -> Mode: + val = config.getoption("asyncio_mode") + if val is None: + val = config.getini("asyncio_mode") + return Mode(val) + + +def pytest_configure(config: Config) -> None: """Inject documentation.""" - config.addinivalue_line("markers", - "asyncio: " - "mark the test as a coroutine, it will be " - "run using an asyncio event loop") + config.addinivalue_line( + "markers", + "asyncio: " + "mark the test as a coroutine, it will be " + "run using an asyncio event loop", + ) + if _get_asyncio_mode(config) == Mode.LEGACY: + config.issue_config_time_warning(LEGACY_MODE, stacklevel=2) @pytest.mark.tryfirst -def pytest_pycollect_makeitem(collector, name, obj): - """A pytest hook to collect asyncio coroutines.""" - if collector.funcnamefilter(name) and _is_coroutine(obj): - item = pytest.Function.from_parent(collector, name=name) - - # Due to how pytest test collection works, module-level pytestmarks - # are applied after the collection step. Since this is the collection - # step, we look ourselves. - transfer_markers(obj, item.cls, item.module) - item = pytest.Function.from_parent(collector, name=name) # To reload keywords. - - if 'asyncio' in item.keywords: - return list(collector._genfunctions(name, obj)) +def pytest_report_header(config: Config) -> List[str]: + """Add asyncio config to pytest header.""" + mode = _get_asyncio_mode(config) + return [f"asyncio: mode={mode}"] -class FixtureStripper: - """Include additional Fixture, and then strip them""" - REQUEST = "request" - EVENT_LOOP = "event_loop" +def _preprocess_async_fixtures(config: Config, holder: Set[FixtureDef]) -> None: + asyncio_mode = _get_asyncio_mode(config) + fixturemanager = config.pluginmanager.get_plugin("funcmanage") + for fixtures in fixturemanager._arg2fixturedefs.values(): + for fixturedef in fixtures: + if fixturedef is holder: + continue + func = fixturedef.func + if not _is_coroutine_or_asyncgen(func): + # Nothing to do with a regular fixture function + continue + if not _has_explicit_asyncio_mark(func): + if asyncio_mode == Mode.STRICT: + # Ignore async fixtures without explicit asyncio mark in strict mode + # This applies to pytest_trio fixtures, for example + continue + elif asyncio_mode == Mode.AUTO: + # Enforce asyncio mode if 'auto' + _set_explicit_asyncio_mark(func) + elif asyncio_mode == Mode.LEGACY: + _set_explicit_asyncio_mark(func) + try: + code = func.__code__ + except AttributeError: + code = func.__func__.__code__ + name = ( + f"" + ) + warnings.warn( + LEGACY_ASYNCIO_FIXTURE.format(name=name), + DeprecationWarning, + ) - def __init__(self, fixturedef): - self.fixturedef = fixturedef - self.to_strip = set() + to_add = [] + for name in ("request", "event_loop"): + if name not in fixturedef.argnames: + to_add.append(name) - def add(self, name): - """Add fixture name to fixturedef - and record in to_strip list (If not previously included)""" - if name in self.fixturedef.argnames: - return - self.fixturedef.argnames += (name, ) - self.to_strip.add(name) + if to_add: + fixturedef.argnames += tuple(to_add) - def get_and_strip_from(self, name, data_dict): - """Strip name from data, and return value""" - result = data_dict[name] - if name in self.to_strip: - del data_dict[name] + if inspect.isasyncgenfunction(func): + fixturedef.func = _wrap_asyncgen(func) + elif inspect.iscoroutinefunction(func): + fixturedef.func = _wrap_async(func) + + assert _has_explicit_asyncio_mark(fixturedef.func) + holder.add(fixturedef) + + +def _add_kwargs( + func: Callable[..., Any], + kwargs: Dict[str, Any], + event_loop: asyncio.AbstractEventLoop, + request: SubRequest, +) -> Dict[str, Any]: + sig = inspect.signature(func) + ret = kwargs.copy() + if "request" in sig.parameters: + ret["request"] = request + if "event_loop" in sig.parameters: + ret["event_loop"] = event_loop + return ret + + +def _wrap_asyncgen(func: Callable[..., AsyncIterator[_R]]) -> Callable[..., _R]: + @functools.wraps(func) + def _asyncgen_fixture_wrapper( + event_loop: asyncio.AbstractEventLoop, request: SubRequest, **kwargs: Any + ) -> _R: + gen_obj = func(**_add_kwargs(func, kwargs, event_loop, request)) + + async def setup() -> _R: + res = await gen_obj.__anext__() + return res + + def finalizer() -> None: + """Yield again, to finalize.""" + + async def async_finalizer() -> None: + try: + await gen_obj.__anext__() + except StopAsyncIteration: + pass + else: + msg = "Async generator fixture didn't stop." + msg += "Yield only once." + raise ValueError(msg) + + event_loop.run_until_complete(async_finalizer()) + + result = event_loop.run_until_complete(setup()) + request.addfinalizer(finalizer) return result + return _asyncgen_fixture_wrapper + + +def _wrap_async(func: Callable[..., Awaitable[_R]]) -> Callable[..., _R]: + @functools.wraps(func) + def _async_fixture_wrapper( + event_loop: asyncio.AbstractEventLoop, request: SubRequest, **kwargs: Any + ) -> _R: + async def setup() -> _R: + res = await func(**_add_kwargs(func, kwargs, event_loop, request)) + return res + + return event_loop.run_until_complete(setup()) + + return _async_fixture_wrapper + + +_HOLDER: Set[FixtureDef] = set() + + +@pytest.mark.tryfirst +def pytest_pycollect_makeitem( + collector: Union[pytest.Module, pytest.Class], name: str, obj: object +) -> Union[ + None, pytest.Item, pytest.Collector, List[Union[pytest.Item, pytest.Collector]] +]: + """A pytest hook to collect asyncio coroutines.""" + if not collector.funcnamefilter(name): + return None + _preprocess_async_fixtures(collector.config, _HOLDER) + if isinstance(obj, staticmethod): + # staticmethods need to be unwrapped. + obj = obj.__func__ + if ( + _is_coroutine(obj) + or _is_hypothesis_test(obj) + and _hypothesis_test_wraps_coroutine(obj) + ): + item = pytest.Function.from_parent(collector, name=name) + marker = item.get_closest_marker("asyncio") + if marker is not None: + return list(collector._genfunctions(name, obj)) + else: + if _get_asyncio_mode(item.config) == Mode.AUTO: + # implicitly add asyncio marker if asyncio mode is on + ret = list(collector._genfunctions(name, obj)) + for elem in ret: + elem.add_marker("asyncio") + return ret # type: ignore[return-value] + return None + + +def _hypothesis_test_wraps_coroutine(function: Any) -> bool: + return _is_coroutine(function.hypothesis.inner_test) + + @pytest.hookimpl(trylast=True) -def pytest_fixture_post_finalizer(fixturedef, request): +def pytest_fixture_post_finalizer(fixturedef: FixtureDef, request: SubRequest) -> None: """Called after fixture teardown""" if fixturedef.argname == "event_loop": - # Set empty loop policy, so that subsequent get_event_loop() provides a new loop - asyncio.set_event_loop_policy(None) - + policy = asyncio.get_event_loop_policy() + try: + loop = policy.get_event_loop() + except RuntimeError: + loop = None + if loop is not None: + # Clean up existing loop to avoid ResourceWarnings + loop.close() + new_loop = policy.new_event_loop() # Replace existing event loop + # Ensure subsequent calls to get_event_loop() succeed + policy.set_event_loop(new_loop) @pytest.hookimpl(hookwrapper=True) -def pytest_fixture_setup(fixturedef, request): +def pytest_fixture_setup( + fixturedef: FixtureDef, request: SubRequest +) -> Optional[object]: """Adjust the event loop policy when an event loop is produced.""" if fixturedef.argname == "event_loop": outcome = yield loop = outcome.get_result() policy = asyncio.get_event_loop_policy() + try: + old_loop = policy.get_event_loop() + if old_loop is not loop: + old_loop.close() + except RuntimeError: + # Swallow this, since it's probably bad event loop hygiene. + pass policy.set_event_loop(loop) return - if isasyncgenfunction(fixturedef.func): - # This is an async generator function. Wrap it accordingly. - generator = fixturedef.func - - fixture_stripper = FixtureStripper(fixturedef) - fixture_stripper.add(FixtureStripper.EVENT_LOOP) - fixture_stripper.add(FixtureStripper.REQUEST) - - - def wrapper(*args, **kwargs): - loop = fixture_stripper.get_and_strip_from(FixtureStripper.EVENT_LOOP, kwargs) - request = fixture_stripper.get_and_strip_from(FixtureStripper.REQUEST, kwargs) - - gen_obj = generator(*args, **kwargs) - - async def setup(): - res = await gen_obj.__anext__() - return res - - def finalizer(): - """Yield again, to finalize.""" - async def async_finalizer(): - try: - await gen_obj.__anext__() - except StopAsyncIteration: - pass - else: - msg = "Async generator fixture didn't stop." - msg += "Yield only once." - raise ValueError(msg) - loop.run_until_complete(async_finalizer()) - - request.addfinalizer(finalizer) - return loop.run_until_complete(setup()) - - fixturedef.func = wrapper - elif inspect.iscoroutinefunction(fixturedef.func): - coro = fixturedef.func - - fixture_stripper = FixtureStripper(fixturedef) - fixture_stripper.add(FixtureStripper.EVENT_LOOP) - - def wrapper(*args, **kwargs): - loop = fixture_stripper.get_and_strip_from(FixtureStripper.EVENT_LOOP, kwargs) - - async def setup(): - res = await coro(*args, **kwargs) - return res - - return loop.run_until_complete(setup()) - - fixturedef.func = wrapper yield @pytest.hookimpl(tryfirst=True, hookwrapper=True) -def pytest_pyfunc_call(pyfuncitem): +def pytest_pyfunc_call(pyfuncitem: pytest.Function) -> Optional[object]: """ - Run asyncio marked test functions in an event loop instead of a normal - function call. + Pytest hook called before a test case is run. + + Wraps marked tests in a synchronous function + where the wrapped test coroutine is executed in an event loop. """ - if 'asyncio' in pyfuncitem.keywords: - if getattr(pyfuncitem.obj, 'is_hypothesis_test', False): + marker = pyfuncitem.get_closest_marker("asyncio") + if marker is not None: + funcargs: Dict[str, object] = pyfuncitem.funcargs # type: ignore[name-defined] + loop = cast(asyncio.AbstractEventLoop, funcargs["event_loop"]) + if _is_hypothesis_test(pyfuncitem.obj): pyfuncitem.obj.hypothesis.inner_test = wrap_in_sync( + pyfuncitem, pyfuncitem.obj.hypothesis.inner_test, - _loop=pyfuncitem.funcargs['event_loop'] + _loop=loop, ) else: pyfuncitem.obj = wrap_in_sync( + pyfuncitem, pyfuncitem.obj, - _loop=pyfuncitem.funcargs['event_loop'] + _loop=loop, ) yield -def wrap_in_sync(func, _loop): +def _is_hypothesis_test(function: Any) -> bool: + return getattr(function, "is_hypothesis_test", False) + + +def wrap_in_sync( + pyfuncitem: pytest.Function, + func: Callable[..., Awaitable[Any]], + _loop: asyncio.AbstractEventLoop, +): """Return a sync wrapper around an async function executing it in the current event loop.""" + # if the function is already wrapped, we rewrap using the original one + # not using __wrapped__ because the original function may already be + # a wrapped one + raw_func = getattr(func, "_raw_test_func", None) + if raw_func is not None: + func = raw_func + @functools.wraps(func) - def inner(**kwargs): - coro = func(**kwargs) - if coro is not None: - task = asyncio.ensure_future(coro, loop=_loop) - try: - _loop.run_until_complete(task) - except BaseException: - # run_until_complete doesn't get the result from exceptions - # that are not subclasses of `Exception`. Consume all - # exceptions to prevent asyncio's warning from logging. - if task.done() and not task.cancelled(): - task.exception() - raise + def inner(*args, **kwargs): + coro = func(*args, **kwargs) + if not inspect.isawaitable(coro): + pyfuncitem.warn( + pytest.PytestWarning( + f"The test {pyfuncitem} is marked with '@pytest.mark.asyncio' " + "but it is not an async function. " + "Please remove asyncio marker. " + "If the test is not marked explicitly, " + "check for global markers applied via 'pytestmark'." + ) + ) + return + task = asyncio.ensure_future(coro, loop=_loop) + try: + _loop.run_until_complete(task) + except BaseException: + # run_until_complete doesn't get the result from exceptions + # that are not subclasses of `Exception`. Consume all + # exceptions to prevent asyncio's warning from logging. + if task.done() and not task.cancelled(): + task.exception() + raise + + inner._raw_test_func = func # type: ignore[attr-defined] return inner -def pytest_runtest_setup(item): - if 'asyncio' in item.keywords: - # inject an event loop fixture for all async tests - if 'event_loop' in item.fixturenames: - item.fixturenames.remove('event_loop') - item.fixturenames.insert(0, 'event_loop') - if item.get_closest_marker("asyncio") is not None \ - and not getattr(item.obj, 'hypothesis', False) \ - and getattr(item.obj, 'is_hypothesis_test', False): - pytest.fail( - 'test function `%r` is using Hypothesis, but pytest-asyncio ' - 'only works with Hypothesis 3.64.0 or later.' % item - ) +def pytest_runtest_setup(item: pytest.Item) -> None: + marker = item.get_closest_marker("asyncio") + if marker is None: + return + fixturenames = item.fixturenames # type: ignore[attr-defined] + # inject an event loop fixture for all async tests + if "event_loop" in fixturenames: + fixturenames.remove("event_loop") + fixturenames.insert(0, "event_loop") + obj = getattr(item, "obj", None) + if not getattr(obj, "hypothesis", False) and getattr( + obj, "is_hypothesis_test", False + ): + pytest.fail( + "test function `%r` is using Hypothesis, but pytest-asyncio " + "only works with Hypothesis 3.64.0 or later." % item + ) @pytest.fixture -def event_loop(request): +def event_loop(request: "pytest.FixtureRequest") -> Iterator[asyncio.AbstractEventLoop]: """Create an instance of the default event loop for each test case.""" loop = asyncio.get_event_loop_policy().new_event_loop() yield loop loop.close() -def _unused_tcp_port(): - """Find an unused localhost TCP port from 1024-65535 and return it.""" - with contextlib.closing(socket.socket()) as sock: - sock.bind(('127.0.0.1', 0)) +def _unused_port(socket_type: int) -> int: + """Find an unused localhost port from 1024-65535 and return it.""" + with contextlib.closing(socket.socket(type=socket_type)) as sock: + sock.bind(("127.0.0.1", 0)) return sock.getsockname()[1] @pytest.fixture -def unused_tcp_port(): - return _unused_tcp_port() +def unused_tcp_port() -> int: + return _unused_port(socket.SOCK_STREAM) @pytest.fixture -def unused_tcp_port_factory(): +def unused_udp_port() -> int: + return _unused_port(socket.SOCK_DGRAM) + + +@pytest.fixture(scope="session") +def unused_tcp_port_factory() -> Callable[[], int]: """A factory function, producing different unused TCP ports.""" produced = set() def factory(): """Return an unused port.""" - port = _unused_tcp_port() + port = _unused_port(socket.SOCK_STREAM) while port in produced: - port = _unused_tcp_port() + port = _unused_port(socket.SOCK_STREAM) produced.add(port) return port + + return factory + + +@pytest.fixture(scope="session") +def unused_udp_port_factory() -> Callable[[], int]: + """A factory function, producing different unused UDP ports.""" + produced = set() + + def factory(): + """Return an unused port.""" + port = _unused_port(socket.SOCK_DGRAM) + + while port in produced: + port = _unused_port(socket.SOCK_DGRAM) + + produced.add(port) + + return port + return factory diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/py.typed b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/pytest_asyncio/py.typed new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/setup.cfg b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/setup.cfg index 23a8eba2ce5..85e3fdb3235 100644 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/setup.cfg +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/setup.cfg @@ -1,5 +1,58 @@ +[metadata] +name = pytest-asyncio +version = attr: pytest_asyncio.__version__ +url = https://github.com/pytest-dev/pytest-asyncio +project_urls = + GitHub = https://github.com/pytest-dev/pytest-asyncio +description = Pytest support for asyncio +long_description = file: README.rst +long_description_content_type = text/x-rst +author = Tin Tvrtković +author_email = tinchester@gmail.com +license = Apache 2.0 +license_file = LICENSE +classifiers = + Development Status :: 4 - Beta + + Intended Audience :: Developers + + License :: OSI Approved :: Apache Software License + + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + + Topic :: Software Development :: Testing + + Framework :: AsyncIO + Framework :: Pytest + Typing :: Typed + +[options] +python_requires = >=3.7 +packages = find: +include_package_data = True +install_requires = + pytest >= 6.1.0 + typing-extensions >= 3.7.2; python_version < "3.8" + +[options.extras_require] +testing = + coverage >= 6.2 + hypothesis >= 5.7.1 + flaky >= 3.5.0 + mypy >= 0.931 + pytest-trio >= 0.7.0 + +[options.entry_points] +pytest11 = + asyncio = pytest_asyncio.plugin + [coverage:run] source = pytest_asyncio +branch = true [coverage:report] show_missing = true @@ -7,10 +60,12 @@ show_missing = true [tool:pytest] addopts = -rsx --tb=short testpaths = tests +asyncio_mode = auto +junit_family = xunit2 filterwarnings = error -[metadata] -license_file = LICENSE +[flake8] +max-line-length = 88 [egg_info] tag_build = diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/setup.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/setup.py deleted file mode 100644 index 61757113507..00000000000 --- a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/setup.py +++ /dev/null @@ -1,54 +0,0 @@ -import re -from pathlib import Path - -from setuptools import setup, find_packages - - -def find_version(): - version_file = ( - Path(__file__) - .parent.joinpath("pytest_asyncio", "__init__.py") - .read_text() - ) - version_match = re.search( - r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M - ) - if version_match: - return version_match.group(1) - - raise RuntimeError("Unable to find version string.") - - -setup( - name="pytest-asyncio", - version=find_version(), - packages=find_packages(), - url="https://github.com/pytest-dev/pytest-asyncio", - license="Apache 2.0", - author="Tin Tvrtković", - author_email="tinchester@gmail.com", - description="Pytest support for asyncio.", - long_description=Path(__file__).parent.joinpath("README.rst").read_text(), - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Topic :: Software Development :: Testing", - "Framework :: Pytest", - ], - python_requires=">= 3.5", - install_requires=["pytest >= 5.4.0"], - extras_require={ - ':python_version == "3.5"': "async_generator >= 1.3", - "testing": [ - "coverage", - "async_generator >= 1.3", - "hypothesis >= 5.7.1", - ], - }, - entry_points={"pytest11": ["asyncio = pytest_asyncio.plugin"]}, -) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_fixtures.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_fixtures.py new file mode 100644 index 00000000000..7ddd04ab86b --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_fixtures.py @@ -0,0 +1,25 @@ +import asyncio +import unittest.mock + +import pytest + +START = object() +END = object() +RETVAL = object() + + +@pytest.fixture +def mock(): + return unittest.mock.Mock(return_value=RETVAL) + + +@pytest.fixture +async def async_fixture(mock): + return await asyncio.sleep(0.1, result=mock(START)) + + +@pytest.mark.asyncio +async def test_async_fixture(async_fixture, mock): + assert mock.call_count == 1 + assert mock.call_args_list[-1] == unittest.mock.call(START) + assert async_fixture is RETVAL diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_fixtures_scope.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_fixtures_scope.py new file mode 100644 index 00000000000..b150f8a8e5d --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_fixtures_scope.py @@ -0,0 +1,25 @@ +""" +We support module-scoped async fixtures, but only if the event loop is +module-scoped too. +""" +import asyncio + +import pytest + + +@pytest.fixture(scope="module") +def event_loop(): + """A module-scoped event loop.""" + return asyncio.new_event_loop() + + +@pytest.fixture(scope="module") +async def async_fixture(): + await asyncio.sleep(0.1) + return 1 + + +@pytest.mark.asyncio +async def test_async_fixture_scope(async_fixture): + assert async_fixture == 1 + await asyncio.sleep(0.1) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_fixtures_with_finalizer.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_fixtures_with_finalizer.py new file mode 100644 index 00000000000..2e72d5de049 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_fixtures_with_finalizer.py @@ -0,0 +1,59 @@ +import asyncio +import functools + +import pytest + + +@pytest.mark.asyncio +async def test_module_with_event_loop_finalizer(port_with_event_loop_finalizer): + await asyncio.sleep(0.01) + assert port_with_event_loop_finalizer + + +@pytest.mark.asyncio +async def test_module_with_get_event_loop_finalizer(port_with_get_event_loop_finalizer): + await asyncio.sleep(0.01) + assert port_with_get_event_loop_finalizer + + +@pytest.fixture(scope="module") +def event_loop(): + """Change event_loop fixture to module level.""" + policy = asyncio.get_event_loop_policy() + loop = policy.new_event_loop() + yield loop + loop.close() + + +@pytest.fixture(scope="module") +async def port_with_event_loop_finalizer(request, event_loop): + def port_finalizer(finalizer): + async def port_afinalizer(): + # await task using loop provided by event_loop fixture + # RuntimeError is raised if task is created on a different loop + await finalizer + + event_loop.run_until_complete(port_afinalizer()) + + worker = asyncio.ensure_future(asyncio.sleep(0.2)) + request.addfinalizer(functools.partial(port_finalizer, worker)) + return True + + +@pytest.fixture(scope="module") +async def port_with_get_event_loop_finalizer(request, event_loop): + def port_finalizer(finalizer): + async def port_afinalizer(): + # await task using current loop retrieved from the event loop policy + # RuntimeError is raised if task is created on a different loop. + # This can happen when pytest_fixture_setup + # does not set up the loop correctly, + # for example when policy.set_event_loop() is called with a wrong argument + await finalizer + + current_loop = asyncio.get_event_loop_policy().get_event_loop() + current_loop.run_until_complete(port_afinalizer()) + + worker = asyncio.ensure_future(asyncio.sleep(0.2)) + request.addfinalizer(functools.partial(port_finalizer, worker)) + return True diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_gen_fixtures.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_gen_fixtures.py new file mode 100644 index 00000000000..0bea7458685 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_async_gen_fixtures.py @@ -0,0 +1,38 @@ +import unittest.mock + +import pytest + +START = object() +END = object() +RETVAL = object() + + +@pytest.fixture(scope="module") +def mock(): + return unittest.mock.Mock(return_value=RETVAL) + + +@pytest.fixture +async def async_gen_fixture(mock): + try: + yield mock(START) + except Exception as e: + mock(e) + else: + mock(END) + + +@pytest.mark.asyncio +async def test_async_gen_fixture(async_gen_fixture, mock): + assert mock.called + assert mock.call_args_list[-1] == unittest.mock.call(START) + assert async_gen_fixture is RETVAL + + +@pytest.mark.asyncio +async def test_async_gen_fixture_finalized(mock): + try: + assert mock.called + assert mock.call_args_list[-1] == unittest.mock.call(END) + finally: + mock.reset_mock() diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_nested.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_nested.py new file mode 100644 index 00000000000..e81e7824520 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_nested.py @@ -0,0 +1,26 @@ +import asyncio + +import pytest + + +@pytest.fixture() +async def async_inner_fixture(): + await asyncio.sleep(0.01) + print("inner start") + yield True + print("inner stop") + + +@pytest.fixture() +async def async_fixture_outer(async_inner_fixture, event_loop): + await asyncio.sleep(0.01) + print("outer start") + assert async_inner_fixture is True + yield True + print("outer stop") + + +@pytest.mark.asyncio +async def test_async_fixture(async_fixture_outer): + assert async_fixture_outer is True + print("test_async_fixture") diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_parametrized_loop.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_parametrized_loop.py new file mode 100644 index 00000000000..2fb8befa7f9 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/async_fixtures/test_parametrized_loop.py @@ -0,0 +1,31 @@ +import asyncio + +import pytest + +TESTS_COUNT = 0 + + +def teardown_module(): + # parametrized 2 * 2 times: 2 for 'event_loop' and 2 for 'fix' + assert TESTS_COUNT == 4 + + +@pytest.fixture(scope="module", params=[1, 2]) +def event_loop(request): + request.param + loop = asyncio.new_event_loop() + yield loop + loop.close() + + +@pytest.fixture(params=["a", "b"]) +async def fix(request): + await asyncio.sleep(0) + return request.param + + +@pytest.mark.asyncio +async def test_parametrized_loop(fix): + await asyncio.sleep(0) + global TESTS_COUNT + TESTS_COUNT += 1 diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/conftest.py new file mode 100644 index 00000000000..4aa8c89aa75 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/conftest.py @@ -0,0 +1,32 @@ +import asyncio + +import pytest + +pytest_plugins = "pytester" + + +@pytest.fixture +def dependent_fixture(event_loop): + """A fixture dependent on the event_loop fixture, doing some cleanup.""" + counter = 0 + + async def just_a_sleep(): + """Just sleep a little while.""" + nonlocal event_loop + await asyncio.sleep(0.1) + nonlocal counter + counter += 1 + + event_loop.run_until_complete(just_a_sleep()) + yield + event_loop.run_until_complete(just_a_sleep()) + + assert counter == 2 + + +@pytest.fixture(scope="session", name="factory_involving_factories") +def factory_involving_factories_fixture(unused_tcp_port_factory): + def factory(): + return unused_tcp_port_factory() + + return factory diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/hypothesis/test_base.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/hypothesis/test_base.py new file mode 100644 index 00000000000..e6da3427329 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/hypothesis/test_base.py @@ -0,0 +1,88 @@ +"""Tests for the Hypothesis integration, which wraps async functions in a +sync shim for Hypothesis. +""" +import asyncio +from textwrap import dedent + +import pytest +from hypothesis import given, strategies as st + + +@pytest.fixture(scope="module") +def event_loop(): + loop = asyncio.get_event_loop_policy().new_event_loop() + yield loop + loop.close() + + +@given(st.integers()) +@pytest.mark.asyncio +async def test_mark_inner(n): + assert isinstance(n, int) + + +@pytest.mark.asyncio +@given(st.integers()) +async def test_mark_outer(n): + assert isinstance(n, int) + + +@pytest.mark.parametrize("y", [1, 2]) +@given(x=st.none()) +@pytest.mark.asyncio +async def test_mark_and_parametrize(x, y): + assert x is None + assert y in (1, 2) + + +@given(st.integers()) +@pytest.mark.asyncio +async def test_can_use_fixture_provided_event_loop(event_loop, n): + semaphore = asyncio.Semaphore(value=0) + event_loop.call_soon(semaphore.release) + await semaphore.acquire() + + +def test_async_auto_marked(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + from hypothesis import given + import hypothesis.strategies as st + + pytest_plugins = 'pytest_asyncio' + + @given(n=st.integers()) + async def test_hypothesis(n: int): + assert isinstance(n, int) + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) + + +def test_sync_not_auto_marked(testdir): + """Assert that synchronous Hypothesis functions are not marked with asyncio""" + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + from hypothesis import given + import hypothesis.strategies as st + + pytest_plugins = 'pytest_asyncio' + + @given(n=st.integers()) + def test_hypothesis(request, n: int): + markers = [marker.name for marker in request.node.own_markers] + assert "asyncio" not in markers + assert isinstance(n, int) + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/hypothesis/test_inherited_test.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/hypothesis/test_inherited_test.py new file mode 100644 index 00000000000..a77622648f7 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/hypothesis/test_inherited_test.py @@ -0,0 +1,20 @@ +import hypothesis.strategies as st +import pytest +from hypothesis import given + + +class BaseClass: + @pytest.mark.asyncio + @given(value=st.integers()) + async def test_hypothesis(self, value: int) -> None: + pass + + +class TestOne(BaseClass): + """During the first execution the Hypothesis test + is wrapped in a synchronous function.""" + + +class TestTwo(BaseClass): + """Execute the test a second time to ensure that + the test receives a fresh event loop.""" diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/loop_fixture_scope/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/loop_fixture_scope/conftest.py new file mode 100644 index 00000000000..223160c2b6c --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/loop_fixture_scope/conftest.py @@ -0,0 +1,17 @@ +import asyncio + +import pytest + + +class CustomSelectorLoop(asyncio.SelectorEventLoop): + """A subclass with no overrides, just to test for presence.""" + + +loop = CustomSelectorLoop() + + +@pytest.fixture(scope="module") +def event_loop(): + """Create an instance of the default event loop for each test case.""" + yield loop + loop.close() diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/loop_fixture_scope/test_loop_fixture_scope.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/loop_fixture_scope/test_loop_fixture_scope.py new file mode 100644 index 00000000000..679ab48f096 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/loop_fixture_scope/test_loop_fixture_scope.py @@ -0,0 +1,16 @@ +"""Unit tests for overriding the event loop with a larger scoped one.""" +import asyncio + +import pytest + + +@pytest.mark.asyncio +async def test_for_custom_loop(): + """This test should be executed using the custom loop.""" + await asyncio.sleep(0.01) + assert type(asyncio.get_event_loop()).__name__ == "CustomSelectorLoop" + + +@pytest.mark.asyncio +async def test_dependent_fixture(dependent_fixture): + await asyncio.sleep(0.1) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/markers/test_class_marker.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/markers/test_class_marker.py new file mode 100644 index 00000000000..d46c3af74f3 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/markers/test_class_marker.py @@ -0,0 +1,25 @@ +"""Test if pytestmark works when defined on a class.""" +import asyncio + +import pytest + + +class TestPyTestMark: + pytestmark = pytest.mark.asyncio + + async def test_is_asyncio(self, event_loop, sample_fixture): + assert asyncio.get_event_loop() + counter = 1 + + async def inc(): + nonlocal counter + counter += 1 + await asyncio.sleep(0) + + await asyncio.ensure_future(inc()) + assert counter == 2 + + +@pytest.fixture +def sample_fixture(): + return None diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/markers/test_module_marker.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/markers/test_module_marker.py new file mode 100644 index 00000000000..2f69dbc9339 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/markers/test_module_marker.py @@ -0,0 +1,39 @@ +"""Test if pytestmark works when defined in a module.""" +import asyncio + +import pytest + +pytestmark = pytest.mark.asyncio + + +class TestPyTestMark: + async def test_is_asyncio(self, event_loop, sample_fixture): + assert asyncio.get_event_loop() + + counter = 1 + + async def inc(): + nonlocal counter + counter += 1 + await asyncio.sleep(0) + + await asyncio.ensure_future(inc()) + assert counter == 2 + + +async def test_is_asyncio(event_loop, sample_fixture): + assert asyncio.get_event_loop() + counter = 1 + + async def inc(): + nonlocal counter + counter += 1 + await asyncio.sleep(0) + + await asyncio.ensure_future(inc()) + assert counter == 2 + + +@pytest.fixture +def sample_fixture(): + return None diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/modes/test_auto_mode.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/modes/test_auto_mode.py new file mode 100644 index 00000000000..fc4d2df076e --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/modes/test_auto_mode.py @@ -0,0 +1,139 @@ +from textwrap import dedent + + +def test_auto_mode_cmdline(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + async def test_a(): + await asyncio.sleep(0) + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) + + +def test_auto_mode_cfg(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + async def test_a(): + await asyncio.sleep(0) + """ + ) + ) + testdir.makefile(".ini", pytest="[pytest]\nasyncio_mode = auto\n") + result = testdir.runpytest() + result.assert_outcomes(passed=1) + + +def test_auto_mode_async_fixture(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + @pytest.fixture + async def fixture_a(): + await asyncio.sleep(0) + return 1 + + async def test_a(fixture_a): + await asyncio.sleep(0) + assert fixture_a == 1 + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) + + +def test_auto_mode_method_fixture(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + + class TestA: + + @pytest.fixture + async def fixture_a(self): + await asyncio.sleep(0) + return 1 + + async def test_a(self, fixture_a): + await asyncio.sleep(0) + assert fixture_a == 1 + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) + + +def test_auto_mode_static_method(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + + pytest_plugins = 'pytest_asyncio' + + + class TestA: + + @staticmethod + async def test_a(): + await asyncio.sleep(0) + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) + + +def test_auto_mode_static_method_fixture(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + + class TestA: + + @staticmethod + @pytest.fixture + async def fixture_a(): + await asyncio.sleep(0) + return 1 + + @staticmethod + async def test_a(fixture_a): + await asyncio.sleep(0) + assert fixture_a == 1 + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/modes/test_legacy_mode.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/modes/test_legacy_mode.py new file mode 100644 index 00000000000..12d4afe18da --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/modes/test_legacy_mode.py @@ -0,0 +1,112 @@ +from textwrap import dedent + +LEGACY_MODE = ( + "The 'asyncio_mode' default value will change to 'strict' in future, " + "please explicitly use 'asyncio_mode=strict' or 'asyncio_mode=auto' " + "in pytest configuration file." +) + +LEGACY_ASYNCIO_FIXTURE = ( + "'@pytest.fixture' is applied to {name} " + "in 'legacy' mode, " + "please replace it with '@pytest_asyncio.fixture' as a preparation " + "for switching to 'strict' mode (or use 'auto' mode to seamlessly handle " + "all these fixtures as asyncio-driven)." +).format(name="*") + + +def test_warning_for_legacy_mode_cmdline(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + @pytest.mark.asyncio + async def test_a(): + await asyncio.sleep(0) + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=legacy") + assert result.parseoutcomes()["warnings"] == 1 + result.stdout.fnmatch_lines(["*" + LEGACY_MODE + "*"]) + + +def test_warning_for_legacy_mode_cfg(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + @pytest.mark.asyncio + async def test_a(): + await asyncio.sleep(0) + """ + ) + ) + testdir.makefile(".ini", pytest="[pytest]\nasyncio_mode = legacy\n") + result = testdir.runpytest() + assert result.parseoutcomes()["warnings"] == 1 + result.stdout.fnmatch_lines(["*" + LEGACY_MODE + "*"]) + result.stdout.no_fnmatch_line("*" + LEGACY_ASYNCIO_FIXTURE + "*") + + +def test_warning_for_legacy_fixture(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + @pytest.fixture + async def fixture_a(): + await asyncio.sleep(0) + return 1 + + @pytest.mark.asyncio + async def test_a(fixture_a): + await asyncio.sleep(0) + assert fixture_a == 1 + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=legacy") + assert result.parseoutcomes()["warnings"] == 2 + result.stdout.fnmatch_lines(["*" + LEGACY_ASYNCIO_FIXTURE + "*"]) + + +def test_warning_for_legacy_method_fixture(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + + class TestA: + + @pytest.fixture + async def fixture_a(self): + await asyncio.sleep(0) + return 1 + + @pytest.mark.asyncio + async def test_a(self, fixture_a): + await asyncio.sleep(0) + assert fixture_a == 1 + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=legacy") + assert result.parseoutcomes()["warnings"] == 2 + result.stdout.fnmatch_lines(["*" + LEGACY_ASYNCIO_FIXTURE + "*"]) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/modes/test_strict_mode.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/modes/test_strict_mode.py new file mode 100644 index 00000000000..3b6487c72b6 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/modes/test_strict_mode.py @@ -0,0 +1,68 @@ +from textwrap import dedent + + +def test_strict_mode_cmdline(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + @pytest.mark.asyncio + async def test_a(): + await asyncio.sleep(0) + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) + + +def test_strict_mode_cfg(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + @pytest.mark.asyncio + async def test_a(): + await asyncio.sleep(0) + """ + ) + ) + testdir.makefile(".ini", pytest="[pytest]\nasyncio_mode = strict\n") + result = testdir.runpytest() + result.assert_outcomes(passed=1) + + +def test_strict_mode_method_fixture(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import pytest + import pytest_asyncio + + pytest_plugins = 'pytest_asyncio' + + class TestA: + + @pytest_asyncio.fixture + async def fixture_a(self): + await asyncio.sleep(0) + return 1 + + @pytest.mark.asyncio + async def test_a(self, fixture_a): + await asyncio.sleep(0) + assert fixture_a == 1 + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/multiloop/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/multiloop/conftest.py new file mode 100644 index 00000000000..ebcb627a6d0 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/multiloop/conftest.py @@ -0,0 +1,15 @@ +import asyncio + +import pytest + + +class CustomSelectorLoop(asyncio.SelectorEventLoop): + """A subclass with no overrides, just to test for presence.""" + + +@pytest.fixture +def event_loop(): + """Create an instance of the default event loop for each test case.""" + loop = CustomSelectorLoop() + yield loop + loop.close() diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/multiloop/test_alternative_loops.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/multiloop/test_alternative_loops.py new file mode 100644 index 00000000000..5f66c96795a --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/multiloop/test_alternative_loops.py @@ -0,0 +1,16 @@ +"""Unit tests for overriding the event loop.""" +import asyncio + +import pytest + + +@pytest.mark.asyncio +async def test_for_custom_loop(): + """This test should be executed using the custom loop.""" + await asyncio.sleep(0.01) + assert type(asyncio.get_event_loop()).__name__ == "CustomSelectorLoop" + + +@pytest.mark.asyncio +async def test_dependent_fixture(dependent_fixture): + await asyncio.sleep(0.1) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/respect_event_loop_policy/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/respect_event_loop_policy/conftest.py new file mode 100644 index 00000000000..2c5cef24ff7 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/respect_event_loop_policy/conftest.py @@ -0,0 +1,16 @@ +"""Defines and sets a custom event loop policy""" +import asyncio +from asyncio import DefaultEventLoopPolicy, SelectorEventLoop + + +class TestEventLoop(SelectorEventLoop): + pass + + +class TestEventLoopPolicy(DefaultEventLoopPolicy): + def new_event_loop(self): + return TestEventLoop() + + +# This statement represents a code which sets a custom event loop policy +asyncio.set_event_loop_policy(TestEventLoopPolicy()) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/respect_event_loop_policy/test_respects_event_loop_policy.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/respect_event_loop_policy/test_respects_event_loop_policy.py new file mode 100644 index 00000000000..610b33889ed --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/respect_event_loop_policy/test_respects_event_loop_policy.py @@ -0,0 +1,17 @@ +"""Tests that any externally provided event loop policy remains unaltered.""" +import asyncio + +import pytest + + +@pytest.mark.asyncio +async def test_uses_loop_provided_by_custom_policy(): + """Asserts that test cases use the event loop + provided by the custom event loop policy""" + assert type(asyncio.get_event_loop()).__name__ == "TestEventLoop" + + +@pytest.mark.asyncio +async def test_custom_policy_is_not_overwritten(): + """Asserts that any custom event loop policy stays the same across test cases""" + assert type(asyncio.get_event_loop()).__name__ == "TestEventLoop" diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_asyncio_fixture.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_asyncio_fixture.py new file mode 100644 index 00000000000..3a28cebb639 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_asyncio_fixture.py @@ -0,0 +1,64 @@ +import asyncio +from textwrap import dedent + +import pytest + +import pytest_asyncio + + +@pytest_asyncio.fixture +async def fixture_bare(): + await asyncio.sleep(0) + return 1 + + +@pytest.mark.asyncio +async def test_bare_fixture(fixture_bare): + await asyncio.sleep(0) + assert fixture_bare == 1 + + +@pytest_asyncio.fixture(name="new_fixture_name") +async def fixture_with_name(request): + await asyncio.sleep(0) + return request.fixturename + + +@pytest.mark.asyncio +async def test_fixture_with_name(new_fixture_name): + await asyncio.sleep(0) + assert new_fixture_name == "new_fixture_name" + + +@pytest_asyncio.fixture(params=[2, 4]) +async def fixture_with_params(request): + await asyncio.sleep(0) + return request.param + + +@pytest.mark.asyncio +async def test_fixture_with_params(fixture_with_params): + await asyncio.sleep(0) + assert fixture_with_params % 2 == 0 + + +@pytest.mark.parametrize("mode", ("auto", "strict", "legacy")) +def test_sync_function_uses_async_fixture(testdir, mode): + testdir.makepyfile( + dedent( + """\ + import pytest_asyncio + + pytest_plugins = 'pytest_asyncio' + + @pytest_asyncio.fixture + async def always_true(): + return True + + def test_sync_function_uses_async_fixture(always_true): + assert always_true is True + """ + ) + ) + result = testdir.runpytest(f"--asyncio-mode={mode}") + result.assert_outcomes(passed=1) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_dependent_fixtures.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_dependent_fixtures.py new file mode 100644 index 00000000000..dc70fe9cd34 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_dependent_fixtures.py @@ -0,0 +1,14 @@ +import asyncio + +import pytest + + +@pytest.mark.asyncio +async def test_dependent_fixture(dependent_fixture): + """Test a dependent fixture.""" + await asyncio.sleep(0.1) + + +@pytest.mark.asyncio +async def test_factory_involving_factories(factory_involving_factories): + factory_involving_factories() diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_event_loop_scope.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_event_loop_scope.py new file mode 100644 index 00000000000..21fd641515c --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_event_loop_scope.py @@ -0,0 +1,37 @@ +"""Test the event loop fixture provides a separate loop for each test. + +These tests need to be run together. +""" +import asyncio + +import pytest + +loop: asyncio.AbstractEventLoop + + +def test_1(): + global loop + # The main thread should have a default event loop. + loop = asyncio.get_event_loop_policy().get_event_loop() + + +@pytest.mark.asyncio +async def test_2(): + global loop + running_loop = asyncio.get_event_loop_policy().get_event_loop() + # Make sure this test case received a different loop + assert running_loop is not loop + loop = running_loop # Store the loop reference for later + + +def test_3(): + global loop + current_loop = asyncio.get_event_loop_policy().get_event_loop() + # Now the event loop from test_2 should have been cleaned up + assert loop is not current_loop + + +def test_4(event_loop): + # If a test sets the loop to None -- pytest_fixture_post_finalizer() + # still should work + asyncio.get_event_loop_policy().set_event_loop(None) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_flaky_integration.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_flaky_integration.py new file mode 100644 index 00000000000..54c9d2eaebd --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_flaky_integration.py @@ -0,0 +1,43 @@ +"""Tests for the Flaky integration, which retries failed tests. +""" +from textwrap import dedent + + +def test_auto_mode_cmdline(testdir): + testdir.makepyfile( + dedent( + """\ + import asyncio + import flaky + import pytest + + _threshold = -1 + + @flaky.flaky(3, 2) + @pytest.mark.asyncio + async def test_asyncio_flaky_thing_that_fails_then_succeeds(): + global _threshold + await asyncio.sleep(0.1) + _threshold += 1 + assert _threshold != 1 + """ + ) + ) + # runpytest_subprocess() is required to don't pollute the output + # with flaky restart information + result = testdir.runpytest_subprocess("--asyncio-mode=strict") + result.assert_outcomes(passed=1) + result.stdout.fnmatch_lines( + [ + "===Flaky Test Report===", + "test_asyncio_flaky_thing_that_fails_then_succeeds passed 1 " + "out of the required 2 times. Running test again until it passes 2 times.", + "test_asyncio_flaky_thing_that_fails_then_succeeds failed " + "(1 runs remaining out of 3).", + " ", + " assert 1 != 1", + "test_asyncio_flaky_thing_that_fails_then_succeeds passed 2 " + "out of the required 2 times. Success!", + "===End Flaky Test Report===", + ] + ) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_simple.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_simple.py new file mode 100644 index 00000000000..dc68d61ec26 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_simple.py @@ -0,0 +1,275 @@ +"""Quick'n'dirty unit tests for provided fixtures and markers.""" +import asyncio +from textwrap import dedent + +import pytest + +import pytest_asyncio.plugin + + +async def async_coro(): + await asyncio.sleep(0) + return "ok" + + +def test_event_loop_fixture(event_loop): + """Test the injection of the event_loop fixture.""" + assert event_loop + ret = event_loop.run_until_complete(async_coro()) + assert ret == "ok" + + +@pytest.mark.asyncio +async def test_asyncio_marker(): + """Test the asyncio pytest marker.""" + await asyncio.sleep(0) + + +@pytest.mark.xfail(reason="need a failure", strict=True) +@pytest.mark.asyncio +async def test_asyncio_marker_fail(): + raise AssertionError + + +@pytest.mark.asyncio +async def test_asyncio_marker_with_default_param(a_param=None): + """Test the asyncio pytest marker.""" + await asyncio.sleep(0) + + +@pytest.mark.asyncio +async def test_unused_port_fixture(unused_tcp_port, event_loop): + """Test the unused TCP port fixture.""" + + async def closer(_, writer): + writer.close() + + server1 = await asyncio.start_server(closer, host="localhost", port=unused_tcp_port) + + with pytest.raises(IOError): + await asyncio.start_server(closer, host="localhost", port=unused_tcp_port) + + server1.close() + await server1.wait_closed() + + +@pytest.mark.asyncio +async def test_unused_udp_port_fixture(unused_udp_port, event_loop): + """Test the unused TCP port fixture.""" + + class Closer: + def connection_made(self, transport): + pass + + def connection_lost(self, *arg, **kwd): + pass + + transport1, _ = await event_loop.create_datagram_endpoint( + Closer, + local_addr=("127.0.0.1", unused_udp_port), + reuse_port=False, + ) + + with pytest.raises(IOError): + await event_loop.create_datagram_endpoint( + Closer, + local_addr=("127.0.0.1", unused_udp_port), + reuse_port=False, + ) + + transport1.abort() + + +@pytest.mark.asyncio +async def test_unused_port_factory_fixture(unused_tcp_port_factory, event_loop): + """Test the unused TCP port factory fixture.""" + + async def closer(_, writer): + writer.close() + + port1, port2, port3 = ( + unused_tcp_port_factory(), + unused_tcp_port_factory(), + unused_tcp_port_factory(), + ) + + server1 = await asyncio.start_server(closer, host="localhost", port=port1) + server2 = await asyncio.start_server(closer, host="localhost", port=port2) + server3 = await asyncio.start_server(closer, host="localhost", port=port3) + + for port in port1, port2, port3: + with pytest.raises(IOError): + await asyncio.start_server(closer, host="localhost", port=port) + + server1.close() + await server1.wait_closed() + server2.close() + await server2.wait_closed() + server3.close() + await server3.wait_closed() + + +@pytest.mark.asyncio +async def test_unused_udp_port_factory_fixture(unused_udp_port_factory, event_loop): + """Test the unused UDP port factory fixture.""" + + class Closer: + def connection_made(self, transport): + pass + + def connection_lost(self, *arg, **kwd): + pass + + port1, port2, port3 = ( + unused_udp_port_factory(), + unused_udp_port_factory(), + unused_udp_port_factory(), + ) + + transport1, _ = await event_loop.create_datagram_endpoint( + Closer, + local_addr=("127.0.0.1", port1), + reuse_port=False, + ) + transport2, _ = await event_loop.create_datagram_endpoint( + Closer, + local_addr=("127.0.0.1", port2), + reuse_port=False, + ) + transport3, _ = await event_loop.create_datagram_endpoint( + Closer, + local_addr=("127.0.0.1", port3), + reuse_port=False, + ) + + for port in port1, port2, port3: + with pytest.raises(IOError): + await event_loop.create_datagram_endpoint( + Closer, + local_addr=("127.0.0.1", port), + reuse_port=False, + ) + + transport1.abort() + transport2.abort() + transport3.abort() + + +def test_unused_port_factory_duplicate(unused_tcp_port_factory, monkeypatch): + """Test correct avoidance of duplicate ports.""" + counter = 0 + + def mock_unused_tcp_port(_ignored): + """Force some duplicate ports.""" + nonlocal counter + counter += 1 + if counter < 5: + return 10000 + else: + return 10000 + counter + + monkeypatch.setattr(pytest_asyncio.plugin, "_unused_port", mock_unused_tcp_port) + + assert unused_tcp_port_factory() == 10000 + assert unused_tcp_port_factory() > 10000 + + +def test_unused_udp_port_factory_duplicate(unused_udp_port_factory, monkeypatch): + """Test correct avoidance of duplicate UDP ports.""" + counter = 0 + + def mock_unused_udp_port(_ignored): + """Force some duplicate ports.""" + nonlocal counter + counter += 1 + if counter < 5: + return 10000 + else: + return 10000 + counter + + monkeypatch.setattr(pytest_asyncio.plugin, "_unused_port", mock_unused_udp_port) + + assert unused_udp_port_factory() == 10000 + assert unused_udp_port_factory() > 10000 + + +class TestMarkerInClassBasedTests: + """Test that asyncio marked functions work for methods of test classes.""" + + @pytest.mark.asyncio + async def test_asyncio_marker_with_explicit_loop_fixture(self, event_loop): + """Test the "asyncio" marker works on a method in + a class-based test with explicit loop fixture.""" + ret = await async_coro() + assert ret == "ok" + + @pytest.mark.asyncio + async def test_asyncio_marker_with_implicit_loop_fixture(self): + """Test the "asyncio" marker works on a method in + a class-based test with implicit loop fixture.""" + ret = await async_coro() + assert ret == "ok" + + +class TestEventLoopStartedBeforeFixtures: + @pytest.fixture + async def loop(self): + return asyncio.get_event_loop() + + @staticmethod + def foo(): + return 1 + + @pytest.mark.asyncio + async def test_no_event_loop(self, loop): + assert await loop.run_in_executor(None, self.foo) == 1 + + @pytest.mark.asyncio + async def test_event_loop_after_fixture(self, loop, event_loop): + assert await loop.run_in_executor(None, self.foo) == 1 + + @pytest.mark.asyncio + async def test_event_loop_before_fixture(self, event_loop, loop): + assert await loop.run_in_executor(None, self.foo) == 1 + + +@pytest.mark.asyncio +async def test_no_warning_on_skip(): + pytest.skip("Test a skip error inside asyncio") + + +def test_async_close_loop(event_loop): + event_loop.close() + return "ok" + + +def test_warn_asyncio_marker_for_regular_func(testdir): + testdir.makepyfile( + dedent( + """\ + import pytest + + pytest_plugins = 'pytest_asyncio' + + @pytest.mark.asyncio + def test_a(): + pass + """ + ) + ) + testdir.makefile( + ".ini", + pytest=dedent( + """\ + [pytest] + asyncio_mode = strict + filterwarnings = + default + """ + ), + ) + result = testdir.runpytest() + result.assert_outcomes(passed=1) + result.stdout.fnmatch_lines( + ["*is marked with '@pytest.mark.asyncio' but it is not an async function.*"] + ) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_subprocess.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_subprocess.py new file mode 100644 index 00000000000..79c5109dab9 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/test_subprocess.py @@ -0,0 +1,36 @@ +"""Tests for using subprocesses in tests.""" +import asyncio.subprocess +import sys + +import pytest + +if sys.platform == "win32": + # The default asyncio event loop implementation on Windows does not + # support subprocesses. Subprocesses are available for Windows if a + # ProactorEventLoop is used. + @pytest.yield_fixture() + def event_loop(): + loop = asyncio.ProactorEventLoop() + yield loop + loop.close() + + +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason=""" + When run with Python 3.7 asyncio.subprocess.create_subprocess_exec seems to be + affected by an issue that prevents correct cleanup. Tests using pytest-trio + will report that signal handling is already performed by another library and + fail. [1] This is possibly a bug in CPython 3.7, so we ignore this test for + that Python version. + + [1] https://github.com/python-trio/pytest-trio/issues/126 + """, +) +@pytest.mark.asyncio +async def test_subprocess(event_loop): + """Starting a subprocess should be possible.""" + proc = await asyncio.subprocess.create_subprocess_exec( + sys.executable, "--version", stdout=asyncio.subprocess.PIPE + ) + await proc.communicate() diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/trio/test_fixtures.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/trio/test_fixtures.py new file mode 100644 index 00000000000..42b28437fd7 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tests/trio/test_fixtures.py @@ -0,0 +1,25 @@ +from textwrap import dedent + + +def test_strict_mode_ignores_trio_fixtures(testdir): + testdir.makepyfile( + dedent( + """\ + import pytest + import pytest_asyncio + import pytest_trio + + pytest_plugins = ["pytest_asyncio", "pytest_trio"] + + @pytest_trio.trio_fixture + async def any_fixture(): + return True + + @pytest.mark.trio + async def test_anything(any_fixture): + pass + """ + ) + ) + result = testdir.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tools/get-version.py b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tools/get-version.py new file mode 100644 index 00000000000..e988a32cb9a --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tools/get-version.py @@ -0,0 +1,17 @@ +import json +import sys +from importlib import metadata + +from packaging.version import parse as parse_version + + +def main(): + version_string = metadata.version("pytest-asyncio") + version = parse_version(version_string) + print(f"::set-output name=version::{version}") + prerelease = json.dumps(version.is_prerelease) + print(f"::set-output name=prerelease::{prerelease}") + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tox.ini b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tox.ini new file mode 100644 index 00000000000..1d8994ae4ca --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/pytest-asyncio/tox.ini @@ -0,0 +1,56 @@ +[tox] +minversion = 3.14.0 +envlist = py37, py38, py39, py310, py311, lint, version-info, pytest-min +isolated_build = true +passenv = + CI + +[testenv] +extras = testing +deps = + --requirement dependencies/default/requirements.txt + --constraint dependencies/default/constraints.txt +commands = make test +allowlist_externals = + make + +[testenv:pytest-min] +extras = testing +deps = + --requirement dependencies/pytest-min/requirements.txt + --constraint dependencies/pytest-min/constraints.txt +commands = make test +allowlist_externals = + make + +[testenv:lint] +basepython = python3.10 +extras = testing +deps = + pre-commit == 2.16.0 +commands = + make lint +allowlist_externals = + make + +[testenv:coverage-report] +deps = coverage +skip_install = true +commands = + coverage combine + coverage report + +[testenv:version-info] +deps = + packaging == 21.3 +commands = + python ./tools/get-version.py + +[gh-actions] +python = + 3.7: py37, pytest-min + 3.8: py38 + 3.9: py39 + 3.10: py310 + 3.11-dev: py311 + pypy3: pypy3 diff --git a/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/LICENSE b/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/LICENSE new file mode 100644 index 00000000000..583f9f6e617 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/LICENSE @@ -0,0 +1,254 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations (now Zope +Corporation, see http://www.zope.com). In 2001, the Python Software +Foundation (PSF, see http://www.python.org/psf/) was formed, a +non-profit organization created specifically to own Python-related +Intellectual Property. Zope Corporation is a sponsoring member of +the PSF. + +All Python releases are Open Source (see http://www.opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are +retained in Python alone or in any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the Internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the Internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/PKG-INFO b/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/PKG-INFO new file mode 100644 index 00000000000..ae2892d3d79 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/PKG-INFO @@ -0,0 +1,35 @@ +Metadata-Version: 2.1 +Name: typing_extensions +Version: 4.1.1 +Summary: Backported and Experimental Type Hints for Python 3.6+ +Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing +Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Classifier: Development Status :: 3 - Alpha +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Software Development +Project-URL: Home, https://github.com/python/typing/blob/master/typing_extensions/README.rst + +Typing Extensions -- Backported and Experimental Type Hints for Python + +The ``typing`` module was added to the standard library in Python 3.5, but +many new features have been added to the module since then. +This means users of older Python versions who are unable to upgrade will not be +able to take advantage of new types added to the ``typing`` module, such as +``typing.Protocol`` or ``typing.TypedDict``. + +The ``typing_extensions`` module contains backports of these changes. +Experimental types that may eventually be added to the ``typing`` +module are also included in ``typing_extensions``. + diff --git a/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/pyproject.toml b/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/pyproject.toml new file mode 100644 index 00000000000..354c2068243 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/pyproject.toml @@ -0,0 +1,63 @@ +# Build system requirements. +[build-system] +requires = ["flit_core >=3.4,<4"] +build-backend = "flit_core.buildapi" + +# Project metadata +[project] +name = "typing_extensions" +version = "4.1.1" +description = "Backported and Experimental Type Hints for Python 3.6+" +readme.text = """\ +Typing Extensions -- Backported and Experimental Type Hints for Python + +The ``typing`` module was added to the standard library in Python 3.5, but +many new features have been added to the module since then. +This means users of older Python versions who are unable to upgrade will not be +able to take advantage of new types added to the ``typing`` module, such as +``typing.Protocol`` or ``typing.TypedDict``. + +The ``typing_extensions`` module contains backports of these changes. +Experimental types that may eventually be added to the ``typing`` +module are also included in ``typing_extensions``. +""" +readme.content-type = "text/x-rst" +requires-python = ">=3.6" +urls.Home = "https://github.com/python/typing/blob/master/typing_extensions/README.rst" +license.file = "LICENSE" +keywords = [ + "annotations", + "backport", + "checker", + "checking", + "function", + "hinting", + "hints", + "type", + "typechecking", + "typehinting", + "typehints", + "typing" +] +# Classifiers list: https://pypi.org/classifiers/ +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: Python Software Foundation License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Topic :: Software Development" +] + +# Project metadata -- authors. Flit stores this as a list of dicts, so it can't +# be inline above. +[[project.authors]] +name = "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" +email = "levkivskyi@gmail.com" diff --git a/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/src/typing_extensions.py b/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/src/typing_extensions.py new file mode 100644 index 00000000000..194731cd3c6 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/third_party/typing_extensions/src/typing_extensions.py @@ -0,0 +1,2908 @@ +import abc +import collections +import collections.abc +import operator +import sys +import types as _types +import typing + +# After PEP 560, internal typing API was substantially reworked. +# This is especially important for Protocol class which uses internal APIs +# quite extensively. +PEP_560 = sys.version_info[:3] >= (3, 7, 0) + +if PEP_560: + GenericMeta = type +else: + # 3.6 + from typing import GenericMeta, _type_vars # noqa + + +# Please keep __all__ alphabetized within each category. +__all__ = [ + # Super-special typing primitives. + 'ClassVar', + 'Concatenate', + 'Final', + 'LiteralString', + 'ParamSpec', + 'Self', + 'Type', + 'TypeVarTuple', + 'Unpack', + + # ABCs (from collections.abc). + 'Awaitable', + 'AsyncIterator', + 'AsyncIterable', + 'Coroutine', + 'AsyncGenerator', + 'AsyncContextManager', + 'ChainMap', + + # Concrete collection types. + 'ContextManager', + 'Counter', + 'Deque', + 'DefaultDict', + 'OrderedDict', + 'TypedDict', + + # Structural checks, a.k.a. protocols. + 'SupportsIndex', + + # One-off things. + 'Annotated', + 'assert_never', + 'dataclass_transform', + 'final', + 'IntVar', + 'is_typeddict', + 'Literal', + 'NewType', + 'overload', + 'Protocol', + 'reveal_type', + 'runtime', + 'runtime_checkable', + 'Text', + 'TypeAlias', + 'TypeGuard', + 'TYPE_CHECKING', + 'Never', + 'NoReturn', + 'Required', + 'NotRequired', +] + +if PEP_560: + __all__.extend(["get_args", "get_origin", "get_type_hints"]) + +# The functions below are modified copies of typing internal helpers. +# They are needed by _ProtocolMeta and they provide support for PEP 646. + + +def _no_slots_copy(dct): + dict_copy = dict(dct) + if '__slots__' in dict_copy: + for slot in dict_copy['__slots__']: + dict_copy.pop(slot, None) + return dict_copy + + +_marker = object() + + +def _check_generic(cls, parameters, elen=_marker): + """Check correct count for parameters of a generic cls (internal helper). + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + if elen is _marker: + if not hasattr(cls, "__parameters__") or not cls.__parameters__: + raise TypeError(f"{cls} is not a generic class") + elen = len(cls.__parameters__) + alen = len(parameters) + if alen != elen: + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) + if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples): + return + raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" + f" actual {alen}, expected {elen}") + + +if sys.version_info >= (3, 10): + def _should_collect_from_parameters(t): + return isinstance( + t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType) + ) +elif sys.version_info >= (3, 9): + def _should_collect_from_parameters(t): + return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) +else: + def _should_collect_from_parameters(t): + return isinstance(t, typing._GenericAlias) and not t._special + + +def _collect_type_vars(types, typevar_types=None): + """Collect all type variable contained in types in order of + first appearance (lexicographic order). For example:: + + _collect_type_vars((T, List[S, T])) == (T, S) + """ + if typevar_types is None: + typevar_types = typing.TypeVar + tvars = [] + for t in types: + if ( + isinstance(t, typevar_types) and + t not in tvars and + not _is_unpack(t) + ): + tvars.append(t) + if _should_collect_from_parameters(t): + tvars.extend([t for t in t.__parameters__ if t not in tvars]) + return tuple(tvars) + + +# 3.6.2+ +if hasattr(typing, 'NoReturn'): + NoReturn = typing.NoReturn +# 3.6.0-3.6.1 +else: + class _NoReturn(typing._FinalTypingBase, _root=True): + """Special type indicating functions that never return. + Example:: + + from typing import NoReturn + + def stop() -> NoReturn: + raise Exception('no way') + + This type is invalid in other positions, e.g., ``List[NoReturn]`` + will fail in static type checkers. + """ + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError("NoReturn cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("NoReturn cannot be used with issubclass().") + + NoReturn = _NoReturn(_root=True) + +# Some unconstrained type variables. These are used by the container types. +# (These are not for export.) +T = typing.TypeVar('T') # Any type. +KT = typing.TypeVar('KT') # Key type. +VT = typing.TypeVar('VT') # Value type. +T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. +T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. + +ClassVar = typing.ClassVar + +# On older versions of typing there is an internal class named "Final". +# 3.8+ +if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7): + Final = typing.Final +# 3.7 +elif sys.version_info[:2] >= (3, 7): + class _FinalForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only single type') + return typing._GenericAlias(self, (item,)) + + Final = _FinalForm('Final', + doc="""A special typing construct to indicate that a name + cannot be re-assigned or overridden in a subclass. + For example: + + MAX_SIZE: Final = 9000 + MAX_SIZE += 1 # Error reported by type checker + + class Connection: + TIMEOUT: Final[int] = 10 + class FastConnector(Connection): + TIMEOUT = 1 # Error reported by type checker + + There is no runtime checking of these properties.""") +# 3.6 +else: + class _Final(typing._FinalTypingBase, _root=True): + """A special typing construct to indicate that a name + cannot be re-assigned or overridden in a subclass. + For example: + + MAX_SIZE: Final = 9000 + MAX_SIZE += 1 # Error reported by type checker + + class Connection: + TIMEOUT: Final[int] = 10 + class FastConnector(Connection): + TIMEOUT = 1 # Error reported by type checker + + There is no runtime checking of these properties. + """ + + __slots__ = ('__type__',) + + def __init__(self, tp=None, **kwds): + self.__type__ = tp + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(typing._type_check(item, + f'{cls.__name__[1:]} accepts only single type.'), + _root=True) + raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted') + + def _eval_type(self, globalns, localns): + new_tp = typing._eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(new_tp, _root=True) + + def __repr__(self): + r = super().__repr__() + if self.__type__ is not None: + r += f'[{typing._type_repr(self.__type__)}]' + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, _Final): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + Final = _Final(_root=True) + + +if sys.version_info >= (3, 11): + final = typing.final +else: + # @final exists in 3.8+, but we backport it for all versions + # before 3.11 to keep support for the __final__ attribute. + # See https://bugs.python.org/issue46342 + def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. The decorator + sets the ``__final__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + """ + try: + f.__final__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return f + + +def IntVar(name): + return typing.TypeVar(name) + + +# 3.8+: +if hasattr(typing, 'Literal'): + Literal = typing.Literal +# 3.7: +elif sys.version_info[:2] >= (3, 7): + class _LiteralForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + return typing._GenericAlias(self, parameters) + + Literal = _LiteralForm('Literal', + doc="""A type that can be used to indicate to type checkers + that the corresponding value has a value literally equivalent + to the provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to + the value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime + checking verifying that the parameter is actually a value + instead of a type.""") +# 3.6: +else: + class _Literal(typing._FinalTypingBase, _root=True): + """A type that can be used to indicate to type checkers that the + corresponding value has a value literally equivalent to the + provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to the + value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime checking + verifying that the parameter is actually a value instead of a type. + """ + + __slots__ = ('__values__',) + + def __init__(self, values=None, **kwds): + self.__values__ = values + + def __getitem__(self, values): + cls = type(self) + if self.__values__ is None: + if not isinstance(values, tuple): + values = (values,) + return cls(values, _root=True) + raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted') + + def _eval_type(self, globalns, localns): + return self + + def __repr__(self): + r = super().__repr__() + if self.__values__ is not None: + r += f'[{", ".join(map(typing._type_repr, self.__values__))}]' + return r + + def __hash__(self): + return hash((type(self).__name__, self.__values__)) + + def __eq__(self, other): + if not isinstance(other, _Literal): + return NotImplemented + if self.__values__ is not None: + return self.__values__ == other.__values__ + return self is other + + Literal = _Literal(_root=True) + + +_overload_dummy = typing._overload_dummy # noqa +overload = typing.overload + + +# This is not a real generic class. Don't use outside annotations. +Type = typing.Type + +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. + + +class _ExtensionsGenericMeta(GenericMeta): + def __subclasscheck__(self, subclass): + """This mimics a more modern GenericMeta.__subclasscheck__() logic + (that does not have problems with recursion) to work around interactions + between collections, typing, and typing_extensions on older + versions of Python, see https://github.com/python/typing/issues/501. + """ + if self.__origin__ is not None: + if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']: + raise TypeError("Parameterized generics cannot be used with class " + "or instance checks") + return False + if not self.__extra__: + return super().__subclasscheck__(subclass) + res = self.__extra__.__subclasshook__(subclass) + if res is not NotImplemented: + return res + if self.__extra__ in subclass.__mro__: + return True + for scls in self.__extra__.__subclasses__(): + if isinstance(scls, GenericMeta): + continue + if issubclass(subclass, scls): + return True + return False + + +Awaitable = typing.Awaitable +Coroutine = typing.Coroutine +AsyncIterable = typing.AsyncIterable +AsyncIterator = typing.AsyncIterator + +# 3.6.1+ +if hasattr(typing, 'Deque'): + Deque = typing.Deque +# 3.6.0 +else: + class Deque(collections.deque, typing.MutableSequence[T], + metaclass=_ExtensionsGenericMeta, + extra=collections.deque): + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is Deque: + return collections.deque(*args, **kwds) + return typing._generic_new(collections.deque, cls, *args, **kwds) + +ContextManager = typing.ContextManager +# 3.6.2+ +if hasattr(typing, 'AsyncContextManager'): + AsyncContextManager = typing.AsyncContextManager +# 3.6.0-3.6.1 +else: + from _collections_abc import _check_methods as _check_methods_in_mro # noqa + + class AsyncContextManager(typing.Generic[T_co]): + __slots__ = () + + async def __aenter__(self): + return self + + @abc.abstractmethod + async def __aexit__(self, exc_type, exc_value, traceback): + return None + + @classmethod + def __subclasshook__(cls, C): + if cls is AsyncContextManager: + return _check_methods_in_mro(C, "__aenter__", "__aexit__") + return NotImplemented + +DefaultDict = typing.DefaultDict + +# 3.7.2+ +if hasattr(typing, 'OrderedDict'): + OrderedDict = typing.OrderedDict +# 3.7.0-3.7.2 +elif (3, 7, 0) <= sys.version_info[:3] < (3, 7, 2): + OrderedDict = typing._alias(collections.OrderedDict, (KT, VT)) +# 3.6 +else: + class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT], + metaclass=_ExtensionsGenericMeta, + extra=collections.OrderedDict): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is OrderedDict: + return collections.OrderedDict(*args, **kwds) + return typing._generic_new(collections.OrderedDict, cls, *args, **kwds) + +# 3.6.2+ +if hasattr(typing, 'Counter'): + Counter = typing.Counter +# 3.6.0-3.6.1 +else: + class Counter(collections.Counter, + typing.Dict[T, int], + metaclass=_ExtensionsGenericMeta, extra=collections.Counter): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is Counter: + return collections.Counter(*args, **kwds) + return typing._generic_new(collections.Counter, cls, *args, **kwds) + +# 3.6.1+ +if hasattr(typing, 'ChainMap'): + ChainMap = typing.ChainMap +elif hasattr(collections, 'ChainMap'): + class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], + metaclass=_ExtensionsGenericMeta, + extra=collections.ChainMap): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is ChainMap: + return collections.ChainMap(*args, **kwds) + return typing._generic_new(collections.ChainMap, cls, *args, **kwds) + +# 3.6.1+ +if hasattr(typing, 'AsyncGenerator'): + AsyncGenerator = typing.AsyncGenerator +# 3.6.0 +else: + class AsyncGenerator(AsyncIterator[T_co], typing.Generic[T_co, T_contra], + metaclass=_ExtensionsGenericMeta, + extra=collections.abc.AsyncGenerator): + __slots__ = () + +NewType = typing.NewType +Text = typing.Text +TYPE_CHECKING = typing.TYPE_CHECKING + + +def _gorg(cls): + """This function exists for compatibility with old typing versions.""" + assert isinstance(cls, GenericMeta) + if hasattr(cls, '_gorg'): + return cls._gorg + while cls.__origin__ is not None: + cls = cls.__origin__ + return cls + + +_PROTO_WHITELIST = ['Callable', 'Awaitable', + 'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator', + 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', + 'ContextManager', 'AsyncContextManager'] + + +def _get_protocol_attrs(cls): + attrs = set() + for base in cls.__mro__[:-1]: # without object + if base.__name__ in ('Protocol', 'Generic'): + continue + annotations = getattr(base, '__annotations__', {}) + for attr in list(base.__dict__.keys()) + list(annotations.keys()): + if (not attr.startswith('_abc_') and attr not in ( + '__abstractmethods__', '__annotations__', '__weakref__', + '_is_protocol', '_is_runtime_protocol', '__dict__', + '__args__', '__slots__', + '__next_in_mro__', '__parameters__', '__origin__', + '__orig_bases__', '__extra__', '__tree_hash__', + '__doc__', '__subclasshook__', '__init__', '__new__', + '__module__', '_MutableMapping__marker', '_gorg')): + attrs.add(attr) + return attrs + + +def _is_callable_members_only(cls): + return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls)) + + +# 3.8+ +if hasattr(typing, 'Protocol'): + Protocol = typing.Protocol +# 3.7 +elif PEP_560: + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + + class _ProtocolMeta(abc.ABCMeta): + # This metaclass is a bit unfortunate and exists only because of the lack + # of __instancehook__. + def __instancecheck__(cls, instance): + # We need this method for situations where attributes are + # assigned in __init__. + if ((not getattr(cls, '_is_protocol', False) or + _is_callable_members_only(cls)) and + issubclass(instance.__class__, cls)): + return True + if cls._is_protocol: + if all(hasattr(instance, attr) and + (not callable(getattr(cls, attr, None)) or + getattr(instance, attr) is not None) + for attr in _get_protocol_attrs(cls)): + return True + return super().__instancecheck__(instance) + + class Protocol(metaclass=_ProtocolMeta): + # There is quite a lot of overlapping code with typing.Generic. + # Unfortunately it is hard to avoid this while these live in two different + # modules. The duplicated code will be removed when Protocol is moved to typing. + """Base class for protocol classes. Protocol classes are defined as:: + + class Proto(Protocol): + def meth(self) -> int: + ... + + Such classes are primarily used with static type checkers that recognize + structural subtyping (static duck-typing), for example:: + + class C: + def meth(self) -> int: + return 0 + + def func(x: Proto) -> int: + return x.meth() + + func(C()) # Passes static type check + + See PEP 544 for details. Protocol classes decorated with + @typing_extensions.runtime act as simple-minded runtime protocol that checks + only the presence of given attributes, ignoring their type signatures. + + Protocol classes can be generic, they are defined as:: + + class GenProto(Protocol[T]): + def meth(self) -> T: + ... + """ + __slots__ = () + _is_protocol = True + + def __new__(cls, *args, **kwds): + if cls is Protocol: + raise TypeError("Type Protocol cannot be instantiated; " + "it can only be used as a base class") + return super().__new__(cls) + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple): + params = (params,) + if not params and cls is not typing.Tuple: + raise TypeError( + f"Parameter list to {cls.__qualname__}[...] cannot be empty") + msg = "Parameters to generic types must be types." + params = tuple(typing._type_check(p, msg) for p in params) # noqa + if cls is Protocol: + # Generic can only be subscripted with unique type variables. + if not all(isinstance(p, typing.TypeVar) for p in params): + i = 0 + while isinstance(params[i], typing.TypeVar): + i += 1 + raise TypeError( + "Parameters to Protocol[...] must all be type variables." + f" Parameter {i + 1} is {params[i]}") + if len(set(params)) != len(params): + raise TypeError( + "Parameters to Protocol[...] must all be unique") + else: + # Subscripting a regular Generic subclass. + _check_generic(cls, params, len(cls.__parameters__)) + return typing._GenericAlias(cls, params) + + def __init_subclass__(cls, *args, **kwargs): + tvars = [] + if '__orig_bases__' in cls.__dict__: + error = typing.Generic in cls.__orig_bases__ + else: + error = typing.Generic in cls.__bases__ + if error: + raise TypeError("Cannot inherit from plain Generic") + if '__orig_bases__' in cls.__dict__: + tvars = typing._collect_type_vars(cls.__orig_bases__) + # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn]. + # If found, tvars must be a subset of it. + # If not found, tvars is it. + # Also check for and reject plain Generic, + # and reject multiple Generic[...] and/or Protocol[...]. + gvars = None + for base in cls.__orig_bases__: + if (isinstance(base, typing._GenericAlias) and + base.__origin__ in (typing.Generic, Protocol)): + # for error messages + the_base = base.__origin__.__name__ + if gvars is not None: + raise TypeError( + "Cannot inherit from Generic[...]" + " and/or Protocol[...] multiple types.") + gvars = base.__parameters__ + if gvars is None: + gvars = tvars + else: + tvarset = set(tvars) + gvarset = set(gvars) + if not tvarset <= gvarset: + s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) + s_args = ', '.join(str(g) for g in gvars) + raise TypeError(f"Some type variables ({s_vars}) are" + f" not listed in {the_base}[{s_args}]") + tvars = gvars + cls.__parameters__ = tuple(tvars) + + # Determine if this is a protocol or a concrete subclass. + if not cls.__dict__.get('_is_protocol', None): + cls._is_protocol = any(b is Protocol for b in cls.__bases__) + + # Set (or override) the protocol subclass hook. + def _proto_hook(other): + if not cls.__dict__.get('_is_protocol', None): + return NotImplemented + if not getattr(cls, '_is_runtime_protocol', False): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Instance and class checks can only be used with" + " @runtime protocols") + if not _is_callable_members_only(cls): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Protocols with non-method members" + " don't support issubclass()") + if not isinstance(other, type): + # Same error as for issubclass(1, int) + raise TypeError('issubclass() arg 1 must be a class') + for attr in _get_protocol_attrs(cls): + for base in other.__mro__: + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + annotations = getattr(base, '__annotations__', {}) + if (isinstance(annotations, typing.Mapping) and + attr in annotations and + isinstance(other, _ProtocolMeta) and + other._is_protocol): + break + else: + return NotImplemented + return True + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = _proto_hook + + # We have nothing more to do for non-protocols. + if not cls._is_protocol: + return + + # Check consistency of bases. + for base in cls.__bases__: + if not (base in (object, typing.Generic) or + base.__module__ == 'collections.abc' and + base.__name__ in _PROTO_WHITELIST or + isinstance(base, _ProtocolMeta) and base._is_protocol): + raise TypeError('Protocols can only inherit from other' + f' protocols, got {repr(base)}') + cls.__init__ = _no_init +# 3.6 +else: + from typing import _next_in_mro, _type_check # noqa + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + + class _ProtocolMeta(GenericMeta): + """Internal metaclass for Protocol. + + This exists so Protocol classes can be generic without deriving + from Generic. + """ + def __new__(cls, name, bases, namespace, + tvars=None, args=None, origin=None, extra=None, orig_bases=None): + # This is just a version copied from GenericMeta.__new__ that + # includes "Protocol" special treatment. (Comments removed for brevity.) + assert extra is None # Protocols should not have extra + if tvars is not None: + assert origin is not None + assert all(isinstance(t, typing.TypeVar) for t in tvars), tvars + else: + tvars = _type_vars(bases) + gvars = None + for base in bases: + if base is typing.Generic: + raise TypeError("Cannot inherit from plain Generic") + if (isinstance(base, GenericMeta) and + base.__origin__ in (typing.Generic, Protocol)): + if gvars is not None: + raise TypeError( + "Cannot inherit from Generic[...] or" + " Protocol[...] multiple times.") + gvars = base.__parameters__ + if gvars is None: + gvars = tvars + else: + tvarset = set(tvars) + gvarset = set(gvars) + if not tvarset <= gvarset: + s_vars = ", ".join(str(t) for t in tvars if t not in gvarset) + s_args = ", ".join(str(g) for g in gvars) + cls_name = "Generic" if any(b.__origin__ is typing.Generic + for b in bases) else "Protocol" + raise TypeError(f"Some type variables ({s_vars}) are" + f" not listed in {cls_name}[{s_args}]") + tvars = gvars + + initial_bases = bases + if (extra is not None and type(extra) is abc.ABCMeta and + extra not in bases): + bases = (extra,) + bases + bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b + for b in bases) + if any(isinstance(b, GenericMeta) and b is not typing.Generic for b in bases): + bases = tuple(b for b in bases if b is not typing.Generic) + namespace.update({'__origin__': origin, '__extra__': extra}) + self = super(GenericMeta, cls).__new__(cls, name, bases, namespace, + _root=True) + super(GenericMeta, self).__setattr__('_gorg', + self if not origin else + _gorg(origin)) + self.__parameters__ = tvars + self.__args__ = tuple(... if a is typing._TypingEllipsis else + () if a is typing._TypingEmpty else + a for a in args) if args else None + self.__next_in_mro__ = _next_in_mro(self) + if orig_bases is None: + self.__orig_bases__ = initial_bases + elif origin is not None: + self._abc_registry = origin._abc_registry + self._abc_cache = origin._abc_cache + if hasattr(self, '_subs_tree'): + self.__tree_hash__ = (hash(self._subs_tree()) if origin else + super(GenericMeta, self).__hash__()) + return self + + def __init__(cls, *args, **kwargs): + super().__init__(*args, **kwargs) + if not cls.__dict__.get('_is_protocol', None): + cls._is_protocol = any(b is Protocol or + isinstance(b, _ProtocolMeta) and + b.__origin__ is Protocol + for b in cls.__bases__) + if cls._is_protocol: + for base in cls.__mro__[1:]: + if not (base in (object, typing.Generic) or + base.__module__ == 'collections.abc' and + base.__name__ in _PROTO_WHITELIST or + isinstance(base, typing.TypingMeta) and base._is_protocol or + isinstance(base, GenericMeta) and + base.__origin__ is typing.Generic): + raise TypeError(f'Protocols can only inherit from other' + f' protocols, got {repr(base)}') + + cls.__init__ = _no_init + + def _proto_hook(other): + if not cls.__dict__.get('_is_protocol', None): + return NotImplemented + if not isinstance(other, type): + # Same error as for issubclass(1, int) + raise TypeError('issubclass() arg 1 must be a class') + for attr in _get_protocol_attrs(cls): + for base in other.__mro__: + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + annotations = getattr(base, '__annotations__', {}) + if (isinstance(annotations, typing.Mapping) and + attr in annotations and + isinstance(other, _ProtocolMeta) and + other._is_protocol): + break + else: + return NotImplemented + return True + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = _proto_hook + + def __instancecheck__(self, instance): + # We need this method for situations where attributes are + # assigned in __init__. + if ((not getattr(self, '_is_protocol', False) or + _is_callable_members_only(self)) and + issubclass(instance.__class__, self)): + return True + if self._is_protocol: + if all(hasattr(instance, attr) and + (not callable(getattr(self, attr, None)) or + getattr(instance, attr) is not None) + for attr in _get_protocol_attrs(self)): + return True + return super(GenericMeta, self).__instancecheck__(instance) + + def __subclasscheck__(self, cls): + if self.__origin__ is not None: + if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']: + raise TypeError("Parameterized generics cannot be used with class " + "or instance checks") + return False + if (self.__dict__.get('_is_protocol', None) and + not self.__dict__.get('_is_runtime_protocol', None)): + if sys._getframe(1).f_globals['__name__'] in ['abc', + 'functools', + 'typing']: + return False + raise TypeError("Instance and class checks can only be used with" + " @runtime protocols") + if (self.__dict__.get('_is_runtime_protocol', None) and + not _is_callable_members_only(self)): + if sys._getframe(1).f_globals['__name__'] in ['abc', + 'functools', + 'typing']: + return super(GenericMeta, self).__subclasscheck__(cls) + raise TypeError("Protocols with non-method members" + " don't support issubclass()") + return super(GenericMeta, self).__subclasscheck__(cls) + + @typing._tp_cache + def __getitem__(self, params): + # We also need to copy this from GenericMeta.__getitem__ to get + # special treatment of "Protocol". (Comments removed for brevity.) + if not isinstance(params, tuple): + params = (params,) + if not params and _gorg(self) is not typing.Tuple: + raise TypeError( + f"Parameter list to {self.__qualname__}[...] cannot be empty") + msg = "Parameters to generic types must be types." + params = tuple(_type_check(p, msg) for p in params) + if self in (typing.Generic, Protocol): + if not all(isinstance(p, typing.TypeVar) for p in params): + raise TypeError( + f"Parameters to {repr(self)}[...] must all be type variables") + if len(set(params)) != len(params): + raise TypeError( + f"Parameters to {repr(self)}[...] must all be unique") + tvars = params + args = params + elif self in (typing.Tuple, typing.Callable): + tvars = _type_vars(params) + args = params + elif self.__origin__ in (typing.Generic, Protocol): + raise TypeError(f"Cannot subscript already-subscripted {repr(self)}") + else: + _check_generic(self, params, len(self.__parameters__)) + tvars = _type_vars(params) + args = params + + prepend = (self,) if self.__origin__ is None else () + return self.__class__(self.__name__, + prepend + self.__bases__, + _no_slots_copy(self.__dict__), + tvars=tvars, + args=args, + origin=self, + extra=self.__extra__, + orig_bases=self.__orig_bases__) + + class Protocol(metaclass=_ProtocolMeta): + """Base class for protocol classes. Protocol classes are defined as:: + + class Proto(Protocol): + def meth(self) -> int: + ... + + Such classes are primarily used with static type checkers that recognize + structural subtyping (static duck-typing), for example:: + + class C: + def meth(self) -> int: + return 0 + + def func(x: Proto) -> int: + return x.meth() + + func(C()) # Passes static type check + + See PEP 544 for details. Protocol classes decorated with + @typing_extensions.runtime act as simple-minded runtime protocol that checks + only the presence of given attributes, ignoring their type signatures. + + Protocol classes can be generic, they are defined as:: + + class GenProto(Protocol[T]): + def meth(self) -> T: + ... + """ + __slots__ = () + _is_protocol = True + + def __new__(cls, *args, **kwds): + if _gorg(cls) is Protocol: + raise TypeError("Type Protocol cannot be instantiated; " + "it can be used only as a base class") + return typing._generic_new(cls.__next_in_mro__, cls, *args, **kwds) + + +# 3.8+ +if hasattr(typing, 'runtime_checkable'): + runtime_checkable = typing.runtime_checkable +# 3.6-3.7 +else: + def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol, so that it + can be used with isinstance() and issubclass(). Raise TypeError + if applied to a non-protocol class. + + This allows a simple-minded structural check very similar to the + one-offs in collections.abc such as Hashable. + """ + if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: + raise TypeError('@runtime_checkable can be only applied to protocol classes,' + f' got {cls!r}') + cls._is_runtime_protocol = True + return cls + + +# Exists for backwards compatibility. +runtime = runtime_checkable + + +# 3.8+ +if hasattr(typing, 'SupportsIndex'): + SupportsIndex = typing.SupportsIndex +# 3.6-3.7 +else: + @runtime_checkable + class SupportsIndex(Protocol): + __slots__ = () + + @abc.abstractmethod + def __index__(self) -> int: + pass + + +if hasattr(typing, "Required"): + # The standard library TypedDict in Python 3.8 does not store runtime information + # about which (if any) keys are optional. See https://bugs.python.org/issue38834 + # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" + # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 + # The standard library TypedDict below Python 3.11 does not store runtime + # information about optional and required keys when using Required or NotRequired. + TypedDict = typing.TypedDict + _TypedDictMeta = typing._TypedDictMeta + is_typeddict = typing.is_typeddict +else: + def _check_fails(cls, other): + try: + if sys._getframe(1).f_globals['__name__'] not in ['abc', + 'functools', + 'typing']: + # Typed dicts are only for static structural subtyping. + raise TypeError('TypedDict does not support instance and class checks') + except (AttributeError, ValueError): + pass + return False + + def _dict_new(*args, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + _, args = args[0], args[1:] # allow the "cls" keyword be passed + return dict(*args, **kwargs) + + _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)' + + def _typeddict_new(*args, total=True, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + _, args = args[0], args[1:] # allow the "cls" keyword be passed + if args: + typename, args = args[0], args[1:] # allow the "_typename" keyword be passed + elif '_typename' in kwargs: + typename = kwargs.pop('_typename') + import warnings + warnings.warn("Passing '_typename' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + raise TypeError("TypedDict.__new__() missing 1 required positional " + "argument: '_typename'") + if args: + try: + fields, = args # allow the "_fields" keyword be passed + except ValueError: + raise TypeError('TypedDict.__new__() takes from 2 to 3 ' + f'positional arguments but {len(args) + 2} ' + 'were given') + elif '_fields' in kwargs and len(kwargs) == 1: + fields = kwargs.pop('_fields') + import warnings + warnings.warn("Passing '_fields' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + fields = None + + if fields is None: + fields = kwargs + elif kwargs: + raise TypeError("TypedDict takes either a dict or keyword arguments," + " but not both") + + ns = {'__annotations__': dict(fields)} + try: + # Setting correct module is necessary to make typed dict classes pickleable. + ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + + return _TypedDictMeta(typename, (), ns, total=total) + + _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,' + ' /, *, total=True, **kwargs)') + + class _TypedDictMeta(type): + def __init__(cls, name, bases, ns, total=True): + super().__init__(name, bases, ns) + + def __new__(cls, name, bases, ns, total=True): + # Create new typed dict class object. + # This method is called directly when TypedDict is subclassed, + # or via _typeddict_new when TypedDict is instantiated. This way + # TypedDict supports all three syntaxes described in its docstring. + # Subclasses and instances of TypedDict return actual dictionaries + # via _dict_new. + ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new + tp_dict = super().__new__(cls, name, (dict,), ns) + + annotations = {} + own_annotations = ns.get('__annotations__', {}) + msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" + own_annotations = { + n: typing._type_check(tp, msg) for n, tp in own_annotations.items() + } + required_keys = set() + optional_keys = set() + + for base in bases: + annotations.update(base.__dict__.get('__annotations__', {})) + required_keys.update(base.__dict__.get('__required_keys__', ())) + optional_keys.update(base.__dict__.get('__optional_keys__', ())) + + annotations.update(own_annotations) + if PEP_560: + for annotation_key, annotation_type in own_annotations.items(): + annotation_origin = get_origin(annotation_type) + if annotation_origin is Annotated: + annotation_args = get_args(annotation_type) + if annotation_args: + annotation_type = annotation_args[0] + annotation_origin = get_origin(annotation_type) + + if annotation_origin is Required: + required_keys.add(annotation_key) + elif annotation_origin is NotRequired: + optional_keys.add(annotation_key) + elif total: + required_keys.add(annotation_key) + else: + optional_keys.add(annotation_key) + else: + own_annotation_keys = set(own_annotations.keys()) + if total: + required_keys.update(own_annotation_keys) + else: + optional_keys.update(own_annotation_keys) + + tp_dict.__annotations__ = annotations + tp_dict.__required_keys__ = frozenset(required_keys) + tp_dict.__optional_keys__ = frozenset(optional_keys) + if not hasattr(tp_dict, '__total__'): + tp_dict.__total__ = total + return tp_dict + + __instancecheck__ = __subclasscheck__ = _check_fails + + TypedDict = _TypedDictMeta('TypedDict', (dict,), {}) + TypedDict.__module__ = __name__ + TypedDict.__doc__ = \ + """A simple typed name space. At runtime it is equivalent to a plain dict. + + TypedDict creates a dictionary type that expects all of its + instances to have a certain set of keys, with each key + associated with a value of a consistent type. This expectation + is not checked at runtime but is only enforced by type checkers. + Usage:: + + class Point2D(TypedDict): + x: int + y: int + label: str + + a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + + assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + + The type info can be accessed via the Point2D.__annotations__ dict, and + the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + TypedDict supports two additional equivalent forms:: + + Point2D = TypedDict('Point2D', x=int, y=int, label=str) + Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) + + The class syntax is only supported in Python 3.6+, while two other + syntax forms work for Python 2.7 and 3.2+ + """ + + if hasattr(typing, "_TypedDictMeta"): + _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) + else: + _TYPEDDICT_TYPES = (_TypedDictMeta,) + + def is_typeddict(tp): + """Check if an annotation is a TypedDict class + + For example:: + class Film(TypedDict): + title: str + year: int + + is_typeddict(Film) # => True + is_typeddict(Union[list, str]) # => False + """ + return isinstance(tp, tuple(_TYPEDDICT_TYPES)) + +if hasattr(typing, "Required"): + get_type_hints = typing.get_type_hints +elif PEP_560: + import functools + import types + + # replaces _strip_annotations() + def _strip_extras(t): + """Strips Annotated, Required and NotRequired from a given type.""" + if isinstance(t, _AnnotatedAlias): + return _strip_extras(t.__origin__) + if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired): + return _strip_extras(t.__args__[0]) + if isinstance(t, typing._GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return t.copy_with(stripped_args) + if hasattr(types, "GenericAlias") and isinstance(t, types.GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return types.GenericAlias(t.__origin__, stripped_args) + if hasattr(types, "UnionType") and isinstance(t, types.UnionType): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return functools.reduce(operator.or_, stripped_args) + + return t + + def get_type_hints(obj, globalns=None, localns=None, include_extras=False): + """Return type hints for an object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, adds Optional[t] if a + default value equal to None is set and recursively replaces all + 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T' + (unless 'include_extras=True'). + + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary. For classes, annotations include also + inherited members. + + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + if hasattr(typing, "Annotated"): + hint = typing.get_type_hints( + obj, globalns=globalns, localns=localns, include_extras=True + ) + else: + hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) + if include_extras: + return hint + return {k: _strip_extras(t) for k, t in hint.items()} + + +# Python 3.9+ has PEP 593 (Annotated) +if hasattr(typing, 'Annotated'): + Annotated = typing.Annotated + # Not exported and not a public API, but needed for get_origin() and get_args() + # to work. + _AnnotatedAlias = typing._AnnotatedAlias +# 3.7-3.8 +elif PEP_560: + class _AnnotatedAlias(typing._GenericAlias, _root=True): + """Runtime representation of an annotated type. + + At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't' + with extra annotations. The alias behaves like a normal typing alias, + instantiating is the same as instantiating the underlying type, binding + it to types is also the same. + """ + def __init__(self, origin, metadata): + if isinstance(origin, _AnnotatedAlias): + metadata = origin.__metadata__ + metadata + origin = origin.__origin__ + super().__init__(origin, origin) + self.__metadata__ = metadata + + def copy_with(self, params): + assert len(params) == 1 + new_type = params[0] + return _AnnotatedAlias(new_type, self.__metadata__) + + def __repr__(self): + return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, " + f"{', '.join(repr(a) for a in self.__metadata__)}]") + + def __reduce__(self): + return operator.getitem, ( + Annotated, (self.__origin__,) + self.__metadata__ + ) + + def __eq__(self, other): + if not isinstance(other, _AnnotatedAlias): + return NotImplemented + if self.__origin__ != other.__origin__: + return False + return self.__metadata__ == other.__metadata__ + + def __hash__(self): + return hash((self.__origin__, self.__metadata__)) + + class Annotated: + """Add context specific metadata to a type. + + Example: Annotated[int, runtime_check.Unsigned] indicates to the + hypothetical runtime_check module that this type is an unsigned int. + Every other consumer of this type can ignore this metadata and treat + this type as int. + + The first argument to Annotated must be a valid type (and will be in + the __origin__ field), the remaining arguments are kept as a tuple in + the __extra__ field. + + Details: + + - It's an error to call `Annotated` with less than two arguments. + - Nested Annotated are flattened:: + + Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] + + - Instantiating an annotated type is equivalent to instantiating the + underlying type:: + + Annotated[C, Ann1](5) == C(5) + + - Annotated can be used as a generic type alias:: + + Optimized = Annotated[T, runtime.Optimize()] + Optimized[int] == Annotated[int, runtime.Optimize()] + + OptimizedList = Annotated[List[T], runtime.Optimize()] + OptimizedList[int] == Annotated[List[int], runtime.Optimize()] + """ + + __slots__ = () + + def __new__(cls, *args, **kwargs): + raise TypeError("Type Annotated cannot be instantiated.") + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple) or len(params) < 2: + raise TypeError("Annotated[...] should be used " + "with at least two arguments (a type and an " + "annotation).") + allowed_special_forms = (ClassVar, Final) + if get_origin(params[0]) in allowed_special_forms: + origin = params[0] + else: + msg = "Annotated[t, ...]: t must be a type." + origin = typing._type_check(params[0], msg) + metadata = tuple(params[1:]) + return _AnnotatedAlias(origin, metadata) + + def __init_subclass__(cls, *args, **kwargs): + raise TypeError( + f"Cannot subclass {cls.__module__}.Annotated" + ) +# 3.6 +else: + + def _is_dunder(name): + """Returns True if name is a __dunder_variable_name__.""" + return len(name) > 4 and name.startswith('__') and name.endswith('__') + + # Prior to Python 3.7 types did not have `copy_with`. A lot of the equality + # checks, argument expansion etc. are done on the _subs_tre. As a result we + # can't provide a get_type_hints function that strips out annotations. + + class AnnotatedMeta(typing.GenericMeta): + """Metaclass for Annotated""" + + def __new__(cls, name, bases, namespace, **kwargs): + if any(b is not object for b in bases): + raise TypeError("Cannot subclass " + str(Annotated)) + return super().__new__(cls, name, bases, namespace, **kwargs) + + @property + def __metadata__(self): + return self._subs_tree()[2] + + def _tree_repr(self, tree): + cls, origin, metadata = tree + if not isinstance(origin, tuple): + tp_repr = typing._type_repr(origin) + else: + tp_repr = origin[0]._tree_repr(origin) + metadata_reprs = ", ".join(repr(arg) for arg in metadata) + return f'{cls}[{tp_repr}, {metadata_reprs}]' + + def _subs_tree(self, tvars=None, args=None): # noqa + if self is Annotated: + return Annotated + res = super()._subs_tree(tvars=tvars, args=args) + # Flatten nested Annotated + if isinstance(res[1], tuple) and res[1][0] is Annotated: + sub_tp = res[1][1] + sub_annot = res[1][2] + return (Annotated, sub_tp, sub_annot + res[2]) + return res + + def _get_cons(self): + """Return the class used to create instance of this type.""" + if self.__origin__ is None: + raise TypeError("Cannot get the underlying type of a " + "non-specialized Annotated type.") + tree = self._subs_tree() + while isinstance(tree, tuple) and tree[0] is Annotated: + tree = tree[1] + if isinstance(tree, tuple): + return tree[0] + else: + return tree + + @typing._tp_cache + def __getitem__(self, params): + if not isinstance(params, tuple): + params = (params,) + if self.__origin__ is not None: # specializing an instantiated type + return super().__getitem__(params) + elif not isinstance(params, tuple) or len(params) < 2: + raise TypeError("Annotated[...] should be instantiated " + "with at least two arguments (a type and an " + "annotation).") + else: + if ( + isinstance(params[0], typing._TypingBase) and + type(params[0]).__name__ == "_ClassVar" + ): + tp = params[0] + else: + msg = "Annotated[t, ...]: t must be a type." + tp = typing._type_check(params[0], msg) + metadata = tuple(params[1:]) + return self.__class__( + self.__name__, + self.__bases__, + _no_slots_copy(self.__dict__), + tvars=_type_vars((tp,)), + # Metadata is a tuple so it won't be touched by _replace_args et al. + args=(tp, metadata), + origin=self, + ) + + def __call__(self, *args, **kwargs): + cons = self._get_cons() + result = cons(*args, **kwargs) + try: + result.__orig_class__ = self + except AttributeError: + pass + return result + + def __getattr__(self, attr): + # For simplicity we just don't relay all dunder names + if self.__origin__ is not None and not _is_dunder(attr): + return getattr(self._get_cons(), attr) + raise AttributeError(attr) + + def __setattr__(self, attr, value): + if _is_dunder(attr) or attr.startswith('_abc_'): + super().__setattr__(attr, value) + elif self.__origin__ is None: + raise AttributeError(attr) + else: + setattr(self._get_cons(), attr, value) + + def __instancecheck__(self, obj): + raise TypeError("Annotated cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("Annotated cannot be used with issubclass().") + + class Annotated(metaclass=AnnotatedMeta): + """Add context specific metadata to a type. + + Example: Annotated[int, runtime_check.Unsigned] indicates to the + hypothetical runtime_check module that this type is an unsigned int. + Every other consumer of this type can ignore this metadata and treat + this type as int. + + The first argument to Annotated must be a valid type, the remaining + arguments are kept as a tuple in the __metadata__ field. + + Details: + + - It's an error to call `Annotated` with less than two arguments. + - Nested Annotated are flattened:: + + Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] + + - Instantiating an annotated type is equivalent to instantiating the + underlying type:: + + Annotated[C, Ann1](5) == C(5) + + - Annotated can be used as a generic type alias:: + + Optimized = Annotated[T, runtime.Optimize()] + Optimized[int] == Annotated[int, runtime.Optimize()] + + OptimizedList = Annotated[List[T], runtime.Optimize()] + OptimizedList[int] == Annotated[List[int], runtime.Optimize()] + """ + +# Python 3.8 has get_origin() and get_args() but those implementations aren't +# Annotated-aware, so we can't use those. Python 3.9's versions don't support +# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do. +if sys.version_info[:2] >= (3, 10): + get_origin = typing.get_origin + get_args = typing.get_args +# 3.7-3.9 +elif PEP_560: + try: + # 3.9+ + from typing import _BaseGenericAlias + except ImportError: + _BaseGenericAlias = typing._GenericAlias + try: + # 3.9+ + from typing import GenericAlias + except ImportError: + GenericAlias = typing._GenericAlias + + def get_origin(tp): + """Get the unsubscripted version of a type. + + This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar + and Annotated. Return None for unsupported types. Examples:: + + get_origin(Literal[42]) is Literal + get_origin(int) is None + get_origin(ClassVar[int]) is ClassVar + get_origin(Generic) is Generic + get_origin(Generic[T]) is Generic + get_origin(Union[T, int]) is Union + get_origin(List[Tuple[T, T]][int]) == list + get_origin(P.args) is P + """ + if isinstance(tp, _AnnotatedAlias): + return Annotated + if isinstance(tp, (typing._GenericAlias, GenericAlias, _BaseGenericAlias, + ParamSpecArgs, ParamSpecKwargs)): + return tp.__origin__ + if tp is typing.Generic: + return typing.Generic + return None + + def get_args(tp): + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if isinstance(tp, _AnnotatedAlias): + return (tp.__origin__,) + tp.__metadata__ + if isinstance(tp, (typing._GenericAlias, GenericAlias)): + if getattr(tp, "_special", False): + return () + res = tp.__args__ + if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: + res = (list(res[:-1]), res[-1]) + return res + return () + + +# 3.10+ +if hasattr(typing, 'TypeAlias'): + TypeAlias = typing.TypeAlias +# 3.9 +elif sys.version_info[:2] >= (3, 9): + class _TypeAliasForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_TypeAliasForm + def TypeAlias(self, parameters): + """Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example above. + """ + raise TypeError(f"{self} is not subscriptable") +# 3.7-3.8 +elif sys.version_info[:2] >= (3, 7): + class _TypeAliasForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + TypeAlias = _TypeAliasForm('TypeAlias', + doc="""Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example + above.""") +# 3.6 +else: + class _TypeAliasMeta(typing.TypingMeta): + """Metaclass for TypeAlias""" + + def __repr__(self): + return 'typing_extensions.TypeAlias' + + class _TypeAliasBase(typing._FinalTypingBase, metaclass=_TypeAliasMeta, _root=True): + """Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example above. + """ + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError("TypeAlias cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("TypeAlias cannot be used with issubclass().") + + def __repr__(self): + return 'typing_extensions.TypeAlias' + + TypeAlias = _TypeAliasBase(_root=True) + + +# Python 3.10+ has PEP 612 +if hasattr(typing, 'ParamSpecArgs'): + ParamSpecArgs = typing.ParamSpecArgs + ParamSpecKwargs = typing.ParamSpecKwargs +# 3.6-3.9 +else: + class _Immutable: + """Mixin to indicate that object should not be copied.""" + __slots__ = () + + def __copy__(self): + return self + + def __deepcopy__(self, memo): + return self + + class ParamSpecArgs(_Immutable): + """The args for a ParamSpec object. + + Given a ParamSpec object P, P.args is an instance of ParamSpecArgs. + + ParamSpecArgs objects have a reference back to their ParamSpec: + + P.args.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.args" + + def __eq__(self, other): + if not isinstance(other, ParamSpecArgs): + return NotImplemented + return self.__origin__ == other.__origin__ + + class ParamSpecKwargs(_Immutable): + """The kwargs for a ParamSpec object. + + Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs. + + ParamSpecKwargs objects have a reference back to their ParamSpec: + + P.kwargs.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.kwargs" + + def __eq__(self, other): + if not isinstance(other, ParamSpecKwargs): + return NotImplemented + return self.__origin__ == other.__origin__ + +# 3.10+ +if hasattr(typing, 'ParamSpec'): + ParamSpec = typing.ParamSpec +# 3.6-3.9 +else: + + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class ParamSpec(list): + """Parameter specification variable. + + Usage:: + + P = ParamSpec('P') + + Parameter specification variables exist primarily for the benefit of static + type checkers. They are used to forward the parameter types of one + callable to another callable, a pattern commonly found in higher order + functions and decorators. They are only valid when used in ``Concatenate``, + or s the first argument to ``Callable``. In Python 3.10 and higher, + they are also supported in user-defined Generics at runtime. + See class Generic for more information on generic types. An + example for annotating a decorator:: + + T = TypeVar('T') + P = ParamSpec('P') + + def add_logging(f: Callable[P, T]) -> Callable[P, T]: + '''A type-safe decorator to add logging to a function.''' + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + logging.info(f'{f.__name__} was called') + return f(*args, **kwargs) + return inner + + @add_logging + def add_two(x: float, y: float) -> float: + '''Add two numbers together.''' + return x + y + + Parameter specification variables defined with covariant=True or + contravariant=True can be used to declare covariant or contravariant + generic types. These keyword arguments are valid, but their actual semantics + are yet to be decided. See PEP 612 for details. + + Parameter specification variables can be introspected. e.g.: + + P.__name__ == 'T' + P.__bound__ == None + P.__covariant__ == False + P.__contravariant__ == False + + Note that only parameter specification variables defined in global scope can + be pickled. + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + @property + def args(self): + return ParamSpecArgs(self) + + @property + def kwargs(self): + return ParamSpecKwargs(self) + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False): + super().__init__([self]) + self.__name__ = name + self.__covariant__ = bool(covariant) + self.__contravariant__ = bool(contravariant) + if bound: + self.__bound__ = typing._type_check(bound, 'Bound must be a type.') + else: + self.__bound__ = None + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __repr__(self): + if self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + # Hack to get typing._type_check to pass. + def __call__(self, *args, **kwargs): + pass + + if not PEP_560: + # Only needed in 3.6. + def _get_type_vars(self, tvars): + if self not in tvars: + tvars.append(self) + + +# 3.6-3.9 +if not hasattr(typing, 'Concatenate'): + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class _ConcatenateGenericAlias(list): + + # Trick Generic into looking into this for __parameters__. + if PEP_560: + __class__ = typing._GenericAlias + else: + __class__ = typing._TypingBase + + # Flag in 3.8. + _special = False + # Attribute in 3.6 and earlier. + _gorg = typing.Generic + + def __init__(self, origin, args): + super().__init__(args) + self.__origin__ = origin + self.__args__ = args + + def __repr__(self): + _type_repr = typing._type_repr + return (f'{_type_repr(self.__origin__)}' + f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]') + + def __hash__(self): + return hash((self.__origin__, self.__args__)) + + # Hack to get typing._type_check to pass in Generic. + def __call__(self, *args, **kwargs): + pass + + @property + def __parameters__(self): + return tuple( + tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) + ) + + if not PEP_560: + # Only required in 3.6. + def _get_type_vars(self, tvars): + if self.__origin__ and self.__parameters__: + typing._get_type_vars(self.__parameters__, tvars) + + +# 3.6-3.9 +@typing._tp_cache +def _concatenate_getitem(self, parameters): + if parameters == (): + raise TypeError("Cannot take a Concatenate of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + if not isinstance(parameters[-1], ParamSpec): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable.") + msg = "Concatenate[arg, ...]: each arg must be a type." + parameters = tuple(typing._type_check(p, msg) for p in parameters) + return _ConcatenateGenericAlias(self, parameters) + + +# 3.10+ +if hasattr(typing, 'Concatenate'): + Concatenate = typing.Concatenate + _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_TypeAliasForm + def Concatenate(self, parameters): + """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """ + return _concatenate_getitem(self, parameters) +# 3.7-8 +elif sys.version_info[:2] >= (3, 7): + class _ConcatenateForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + return _concatenate_getitem(self, parameters) + + Concatenate = _ConcatenateForm( + 'Concatenate', + doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """) +# 3.6 +else: + class _ConcatenateAliasMeta(typing.TypingMeta): + """Metaclass for Concatenate.""" + + def __repr__(self): + return 'typing_extensions.Concatenate' + + class _ConcatenateAliasBase(typing._FinalTypingBase, + metaclass=_ConcatenateAliasMeta, + _root=True): + """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """ + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError("Concatenate cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("Concatenate cannot be used with issubclass().") + + def __repr__(self): + return 'typing_extensions.Concatenate' + + def __getitem__(self, parameters): + return _concatenate_getitem(self, parameters) + + Concatenate = _ConcatenateAliasBase(_root=True) + +# 3.10+ +if hasattr(typing, 'TypeGuard'): + TypeGuard = typing.TypeGuard +# 3.9 +elif sys.version_info[:2] >= (3, 9): + class _TypeGuardForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_TypeGuardForm + def TypeGuard(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """ + item = typing._type_check(parameters, f'{self} accepts only single type.') + return typing._GenericAlias(self, (item,)) +# 3.7-3.8 +elif sys.version_info[:2] >= (3, 7): + class _TypeGuardForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + TypeGuard = _TypeGuardForm( + 'TypeGuard', + doc="""Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """) +# 3.6 +else: + class _TypeGuard(typing._FinalTypingBase, _root=True): + """Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """ + + __slots__ = ('__type__',) + + def __init__(self, tp=None, **kwds): + self.__type__ = tp + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(typing._type_check(item, + f'{cls.__name__[1:]} accepts only a single type.'), + _root=True) + raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted') + + def _eval_type(self, globalns, localns): + new_tp = typing._eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(new_tp, _root=True) + + def __repr__(self): + r = super().__repr__() + if self.__type__ is not None: + r += f'[{typing._type_repr(self.__type__)}]' + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, _TypeGuard): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + TypeGuard = _TypeGuard(_root=True) + + +if sys.version_info[:2] >= (3, 7): + # Vendored from cpython typing._SpecialFrom + class _SpecialForm(typing._Final, _root=True): + __slots__ = ('_name', '__doc__', '_getitem') + + def __init__(self, getitem): + self._getitem = getitem + self._name = getitem.__name__ + self.__doc__ = getitem.__doc__ + + def __getattr__(self, item): + if item in {'__name__', '__qualname__'}: + return self._name + + raise AttributeError(item) + + def __mro_entries__(self, bases): + raise TypeError(f"Cannot subclass {self!r}") + + def __repr__(self): + return f'typing_extensions.{self._name}' + + def __reduce__(self): + return self._name + + def __call__(self, *args, **kwds): + raise TypeError(f"Cannot instantiate {self!r}") + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance()") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass()") + + @typing._tp_cache + def __getitem__(self, parameters): + return self._getitem(self, parameters) + + +if hasattr(typing, "LiteralString"): + LiteralString = typing.LiteralString +elif sys.version_info[:2] >= (3, 7): + @_SpecialForm + def LiteralString(self, params): + """Represents an arbitrary literal string. + + Example:: + + from typing_extensions import LiteralString + + def query(sql: LiteralString) -> ...: + ... + + query("SELECT * FROM table") # ok + query(f"SELECT * FROM {input()}") # not ok + + See PEP 675 for details. + + """ + raise TypeError(f"{self} is not subscriptable") +else: + class _LiteralString(typing._FinalTypingBase, _root=True): + """Represents an arbitrary literal string. + + Example:: + + from typing_extensions import LiteralString + + def query(sql: LiteralString) -> ...: + ... + + query("SELECT * FROM table") # ok + query(f"SELECT * FROM {input()}") # not ok + + See PEP 675 for details. + + """ + + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass().") + + LiteralString = _LiteralString(_root=True) + + +if hasattr(typing, "Self"): + Self = typing.Self +elif sys.version_info[:2] >= (3, 7): + @_SpecialForm + def Self(self, params): + """Used to spell the type of "self" in classes. + + Example:: + + from typing import Self + + class ReturnsSelf: + def parse(self, data: bytes) -> Self: + ... + return self + + """ + + raise TypeError(f"{self} is not subscriptable") +else: + class _Self(typing._FinalTypingBase, _root=True): + """Used to spell the type of "self" in classes. + + Example:: + + from typing import Self + + class ReturnsSelf: + def parse(self, data: bytes) -> Self: + ... + return self + + """ + + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass().") + + Self = _Self(_root=True) + + +if hasattr(typing, "Never"): + Never = typing.Never +elif sys.version_info[:2] >= (3, 7): + @_SpecialForm + def Never(self, params): + """The bottom type, a type that has no members. + + This can be used to define a function that should never be + called, or a function that never returns:: + + from typing_extensions import Never + + def never_call_me(arg: Never) -> None: + pass + + def int_or_str(arg: int | str) -> None: + never_call_me(arg) # type checker error + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + never_call_me(arg) # ok, arg is of type Never + + """ + + raise TypeError(f"{self} is not subscriptable") +else: + class _Never(typing._FinalTypingBase, _root=True): + """The bottom type, a type that has no members. + + This can be used to define a function that should never be + called, or a function that never returns:: + + from typing_extensions import Never + + def never_call_me(arg: Never) -> None: + pass + + def int_or_str(arg: int | str) -> None: + never_call_me(arg) # type checker error + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + never_call_me(arg) # ok, arg is of type Never + + """ + + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass().") + + Never = _Never(_root=True) + + +if hasattr(typing, 'Required'): + Required = typing.Required + NotRequired = typing.NotRequired +elif sys.version_info[:2] >= (3, 9): + class _ExtensionsSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_ExtensionsSpecialForm + def Required(self, parameters): + """A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """ + item = typing._type_check(parameters, f'{self._name} accepts only single type') + return typing._GenericAlias(self, (item,)) + + @_ExtensionsSpecialForm + def NotRequired(self, parameters): + """A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """ + item = typing._type_check(parameters, f'{self._name} accepts only single type') + return typing._GenericAlias(self, (item,)) + +elif sys.version_info[:2] >= (3, 7): + class _RequiredForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + '{} accepts only single type'.format(self._name)) + return typing._GenericAlias(self, (item,)) + + Required = _RequiredForm( + 'Required', + doc="""A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """) + NotRequired = _RequiredForm( + 'NotRequired', + doc="""A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """) +else: + # NOTE: Modeled after _Final's implementation when _FinalTypingBase available + class _MaybeRequired(typing._FinalTypingBase, _root=True): + __slots__ = ('__type__',) + + def __init__(self, tp=None, **kwds): + self.__type__ = tp + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(typing._type_check(item, + '{} accepts only single type.'.format(cls.__name__[1:])), + _root=True) + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + def _eval_type(self, globalns, localns): + new_tp = typing._eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(new_tp, _root=True) + + def __repr__(self): + r = super().__repr__() + if self.__type__ is not None: + r += '[{}]'.format(typing._type_repr(self.__type__)) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, type(self)): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + class _Required(_MaybeRequired, _root=True): + """A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """ + + class _NotRequired(_MaybeRequired, _root=True): + """A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """ + + Required = _Required(_root=True) + NotRequired = _NotRequired(_root=True) + + +if sys.version_info[:2] >= (3, 9): + class _UnpackSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + @_UnpackSpecialForm + def Unpack(self, parameters): + """A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... + + """ + item = typing._type_check(parameters, f'{self._name} accepts only single type') + return _UnpackAlias(self, (item,)) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + +elif sys.version_info[:2] >= (3, 7): + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + class _UnpackForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only single type') + return _UnpackAlias(self, (item,)) + + Unpack = _UnpackForm( + 'Unpack', + doc="""A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... + + """) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + +else: + # NOTE: Modeled after _Final's implementation when _FinalTypingBase available + class _Unpack(typing._FinalTypingBase, _root=True): + """A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... + + """ + __slots__ = ('__type__',) + __class__ = typing.TypeVar + + def __init__(self, tp=None, **kwds): + self.__type__ = tp + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(typing._type_check(item, + 'Unpack accepts only single type.'), + _root=True) + raise TypeError('Unpack cannot be further subscripted') + + def _eval_type(self, globalns, localns): + new_tp = typing._eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(new_tp, _root=True) + + def __repr__(self): + r = super().__repr__() + if self.__type__ is not None: + r += '[{}]'.format(typing._type_repr(self.__type__)) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, _Unpack): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + # For 3.6 only + def _get_type_vars(self, tvars): + self.__type__._get_type_vars(tvars) + + Unpack = _Unpack(_root=True) + + def _is_unpack(obj): + return isinstance(obj, _Unpack) + + +class TypeVarTuple: + """Type variable tuple. + + Usage:: + + Ts = TypeVarTuple('Ts') + + In the same way that a normal type variable is a stand-in for a single + type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* type such as + ``Tuple[int, str]``. + + Type variable tuples can be used in ``Generic`` declarations. + Consider the following example:: + + class Array(Generic[*Ts]): ... + + The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``, + where ``T1`` and ``T2`` are type variables. To use these type variables + as type parameters of ``Array``, we must *unpack* the type variable tuple using + the star operator: ``*Ts``. The signature of ``Array`` then behaves + as if we had simply written ``class Array(Generic[T1, T2]): ...``. + In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows + us to parameterise the class with an *arbitrary* number of type parameters. + + Type variable tuples can be used anywhere a normal ``TypeVar`` can. + This includes class definitions, as shown above, as well as function + signatures and variable annotations:: + + class Array(Generic[*Ts]): + + def __init__(self, shape: Tuple[*Ts]): + self._shape: Tuple[*Ts] = shape + + def get_shape(self) -> Tuple[*Ts]: + return self._shape + + shape = (Height(480), Width(640)) + x: Array[Height, Width] = Array(shape) + y = abs(x) # Inferred type is Array[Height, Width] + z = x + x # ... is Array[Height, Width] + x.get_shape() # ... is tuple[Height, Width] + + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + def __iter__(self): + yield self.__unpacked__ + + def __init__(self, name): + self.__name__ = name + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + self.__unpacked__ = Unpack[self] + + def __repr__(self): + return self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(self, *args, **kwds): + if '_root' not in kwds: + raise TypeError("Cannot subclass special typing classes") + + if not PEP_560: + # Only needed in 3.6. + def _get_type_vars(self, tvars): + if self not in tvars: + tvars.append(self) + + +if hasattr(typing, "reveal_type"): + reveal_type = typing.reveal_type +else: + def reveal_type(__obj: T) -> T: + """Reveal the inferred type of a variable. + + When a static type checker encounters a call to ``reveal_type()``, + it will emit the inferred type of the argument:: + + x: int = 1 + reveal_type(x) + + Running a static type checker (e.g., ``mypy``) on this example + will produce output similar to 'Revealed type is "builtins.int"'. + + At runtime, the function prints the runtime type of the + argument and returns it unchanged. + + """ + print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr) + return __obj + + +if hasattr(typing, "assert_never"): + assert_never = typing.assert_never +else: + def assert_never(__arg: Never) -> Never: + """Assert to the type checker that a line of code is unreachable. + + Example:: + + def int_or_str(arg: int | str) -> None: + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + assert_never(arg) + + If a type checker finds that a call to assert_never() is + reachable, it will emit an error. + + At runtime, this throws an exception when called. + + """ + raise AssertionError("Expected code to be unreachable") + + +if hasattr(typing, 'dataclass_transform'): + dataclass_transform = typing.dataclass_transform +else: + def dataclass_transform( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + field_descriptors: typing.Tuple[ + typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]], + ... + ] = (), + ) -> typing.Callable[[T], T]: + """Decorator that marks a function, class, or metaclass as providing + dataclass-like behavior. + + Example: + + from typing_extensions import dataclass_transform + + _T = TypeVar("_T") + + # Used on a decorator function + @dataclass_transform() + def create_model(cls: type[_T]) -> type[_T]: + ... + return cls + + @create_model + class CustomerModel: + id: int + name: str + + # Used on a base class + @dataclass_transform() + class ModelBase: ... + + class CustomerModel(ModelBase): + id: int + name: str + + # Used on a metaclass + @dataclass_transform() + class ModelMeta(type): ... + + class ModelBase(metaclass=ModelMeta): ... + + class CustomerModel(ModelBase): + id: int + name: str + + Each of the ``CustomerModel`` classes defined in this example will now + behave similarly to a dataclass created with the ``@dataclasses.dataclass`` + decorator. For example, the type checker will synthesize an ``__init__`` + method. + + The arguments to this decorator can be used to customize this behavior: + - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be + True or False if it is omitted by the caller. + - ``order_default`` indicates whether the ``order`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``kw_only_default`` indicates whether the ``kw_only`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``field_descriptors`` specifies a static list of supported classes + or functions, that describe fields, similar to ``dataclasses.field()``. + + At runtime, this decorator records its arguments in the + ``__dataclass_transform__`` attribute on the decorated object. + + See PEP 681 for details. + + """ + def decorator(cls_or_fn): + cls_or_fn.__dataclass_transform__ = { + "eq_default": eq_default, + "order_default": order_default, + "kw_only_default": kw_only_default, + "field_descriptors": field_descriptors, + } + return cls_or_fn + return decorator + + +# We have to do some monkey patching to deal with the dual nature of +# Unpack/TypeVarTuple: +# - We want Unpack to be a kind of TypeVar so it gets accepted in +# Generic[Unpack[Ts]] +# - We want it to *not* be treated as a TypeVar for the purposes of +# counting generic parameters, so that when we subscript a generic, +# the runtime doesn't try to substitute the Unpack with the subscripted type. +if not hasattr(typing, "TypeVarTuple"): + typing._collect_type_vars = _collect_type_vars + typing._check_generic = _check_generic diff --git a/tests/wpt/web-platform-tests/tools/webdriver/webdriver/bidi/transport.py b/tests/wpt/web-platform-tests/tools/webdriver/webdriver/bidi/transport.py index afe054528e8..a28c484b83c 100644 --- a/tests/wpt/web-platform-tests/tools/webdriver/webdriver/bidi/transport.py +++ b/tests/wpt/web-platform-tests/tools/webdriver/webdriver/bidi/transport.py @@ -37,7 +37,7 @@ class Transport: self.read_message_task: Optional[asyncio.Task[Any]] = None async def start(self) -> None: - self.connection = await websockets.client.connect(self.url) + self.connection = await websockets.connect(self.url) self.read_message_task = self.loop.create_task(self.read_messages()) for msg in self.send_buf: diff --git a/tests/wpt/web-platform-tests/tools/webtransport/h3/webtransport_h3_server.py b/tests/wpt/web-platform-tests/tools/webtransport/h3/webtransport_h3_server.py index 9d62f09926f..c59ffcbffc9 100644 --- a/tests/wpt/web-platform-tests/tools/webtransport/h3/webtransport_h3_server.py +++ b/tests/wpt/web-platform-tests/tools/webtransport/h3/webtransport_h3_server.py @@ -4,6 +4,7 @@ import asyncio import logging import os import ssl +import sys import threading import traceback from urllib.parse import urlparse @@ -502,8 +503,15 @@ class WebTransportH3Server: ticket_store = SessionTicketStore() + # On Windows, the default event loop is ProactorEventLoop but it + # doesn't seem to work when aioquic detects a connection loss. + # Use SelectorEventLoop to work around the problem. + if sys.platform == "win32": + asyncio.set_event_loop_policy( + asyncio.WindowsSelectorEventLoopPolicy()) self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) + self.loop.run_until_complete( serve( self.host, diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/environment.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/environment.py index 7edc68f998d..2bf72448e79 100644 --- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/environment.py +++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/environment.py @@ -223,7 +223,7 @@ class TestEnvironment: for path, format_args, content_type, route in [ ("testharness_runner.html", {}, "text/html", "/testharness_runner.html"), - ("print_reftest_runner.html", {}, "text/html", "/print_reftest_runner.html"), + ("print_pdf_runner.html", {}, "text/html", "/print_pdf_runner.html"), (os.path.join(here, "..", "..", "third_party", "pdf_js", "pdf.js"), None, "text/javascript", "/_pdf_js/pdf.js"), (os.path.join(here, "..", "..", "third_party", "pdf_js", "pdf.worker.js"), None, diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executorchrome.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executorchrome.py index e5f5615385a..030578c7b93 100644 --- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executorchrome.py +++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executorchrome.py @@ -18,7 +18,7 @@ class ChromeDriverPrintProtocolPart(PrintProtocolPart): self.runner_handle = None def load_runner(self): - url = urljoin(self.parent.executor.server_url("http"), "/print_reftest_runner.html") + url = urljoin(self.parent.executor.server_url("http"), "/print_pdf_runner.html") self.logger.debug("Loading %s" % url) try: self.webdriver.url = url diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executorcontentshell.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executorcontentshell.py index 474bb7168e3..e00f25b0038 100644 --- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executorcontentshell.py +++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executorcontentshell.py @@ -264,6 +264,15 @@ class ContentShellTestharnessExecutor(TestharnessExecutor): if not text: return (test.result_cls("ERROR", errors), []) - return self.convert_result(test, json.loads(text)) + result_url, status, message, stack, subtest_results = json.loads(text) + if result_url != test.url: + # Suppress `convert_result`'s URL validation. + # See `testharnessreport-content-shell.js` for details. + self.logger.warning('Got results from %s, expected %s' % (result_url, test.url)) + self.logger.warning('URL mismatch may be a false positive ' + 'if the test navigates') + result_url = test.url + raw_result = result_url, status, message, stack, subtest_results + return self.convert_result(test, raw_result) except BaseException as exception: return _convert_exception(test, exception, self.protocol.content_shell_errors.read_errors()) diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py index 6fafca8762a..aa6d50bc87b 100644 --- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py +++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py @@ -628,7 +628,7 @@ class MarionettePrintProtocolPart(PrintProtocolPart): self.runner_handle = None def load_runner(self): - url = urljoin(self.parent.executor.server_url("http"), "/print_reftest_runner.html") + url = urljoin(self.parent.executor.server_url("http"), "/print_pdf_runner.html") self.logger.debug("Loading %s" % url) try: self.marionette.navigate(url) @@ -655,7 +655,7 @@ class MarionettePrintProtocolPart(PrintProtocolPart): "bottom": margin, }, "shrinkToFit": False, - "printBackground": True, + "background": True, } return self.marionette._send_message("WebDriver:Print", body, key="value") diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/print_reftest_runner.html b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/print_pdf_runner.html similarity index 100% rename from tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/print_reftest_runner.html rename to tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/print_pdf_runner.html diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testharnessreport-content-shell.js b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testharnessreport-content-shell.js index e4693f9bc22..1e24fdbcfbf 100644 --- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testharnessreport-content-shell.js +++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testharnessreport-content-shell.js @@ -1,25 +1,45 @@ -var props = {output:%(output)d, debug: %(debug)s}; -var start_loc = document.createElement('a'); -start_loc.href = location.href; -setup(props); +(function() { + var props = {output:%(output)d, debug: %(debug)s}; + setup(props); -testRunner.dumpAsText(); -testRunner.waitUntilDone(); -testRunner.setPopupBlockingEnabled(false); -testRunner.setDumpJavaScriptDialogs(false); + // Some tests navigate away from the original URL as part of the + // functionality they exercise. In that case, `add_completion_callback(...)` + // uses the final `window.location` to report the test ID, which may not be + // correct [1]. + // + // Persisting the original `window.location` with standard web platform APIs + // (e.g., `localStorage`) could interfere with the their tests, so this must + // be avoided. Unfortunately, there doesn't appear to be anything in content + // shell's protocol mode or Blink-specific `window.testRunner` or + // `window.internals` [2] that could help with this. As such, the driver + // simply downgrades a mismatched test ID to a logged warning instead of a + // harness error. + // + // [1] crbug.com/1418753 + // [2] https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/testing/writing_web_tests.md#Relying-on-Blink_Specific-Testing-APIs + const url = new URL(location.href); -add_completion_callback(function (tests, harness_status) { - var id = decodeURIComponent(start_loc.pathname) + decodeURIComponent(start_loc.search) + decodeURIComponent(start_loc.hash); - var result_string = JSON.stringify([ - id, - harness_status.status, - harness_status.message, - harness_status.stack, - tests.map(function(t) { - return [t.name, t.status, t.message, t.stack] - }), - ]); + testRunner.dumpAsText(); + testRunner.waitUntilDone(); + testRunner.setPopupBlockingEnabled(false); + testRunner.setDumpJavaScriptDialogs(false); + // Show `CONSOLE MESSAGE:` and `CONSOLE ERROR:` in stderr. + if (props.debug) { + testRunner.setDumpConsoleMessages(true); + } - testRunner.setCustomTextOutput(result_string); - testRunner.notifyDone(); -}); + add_completion_callback(function (tests, harness_status) { + const test_id = decodeURIComponent(url.pathname) + decodeURIComponent(url.search) + decodeURIComponent(url.hash); + const result_string = JSON.stringify([ + test_id, + harness_status.status, + harness_status.message, + harness_status.stack, + tests.map(function(t) { + return [t.name, t.status, t.message, t.stack] + }), + ]); + testRunner.setCustomTextOutput(result_string); + testRunner.notifyDone(); + }); +})(); diff --git a/tests/wpt/web-platform-tests/top-level-storage-access-api/README.md b/tests/wpt/web-platform-tests/top-level-storage-access-api/README.md index 31a32b8033e..e3636a221ec 100644 --- a/tests/wpt/web-platform-tests/top-level-storage-access-api/README.md +++ b/tests/wpt/web-platform-tests/top-level-storage-access-api/README.md @@ -1,4 +1,4 @@ -# requestStorageAccessForOrigin Tests -These tests are tentative. They are based on a proposed requestStorageAccessForOrigin extension to the Storage Access API which can be read about [in the explainer](https://github.com/privacycg/requestStorageAccessForOrigin). +# requestStorageAccessFor Tests +These tests are tentative. They are based on a proposed requestStorageAccessFor extension to the Storage Access API which can be read about [in the explainer](https://github.com/privacycg/requestStorageAccessForOrigin). Note that the spec is in progress, and available [rendered](https://privacycg.github.io/requestStorageAccessForOrigin/) and [in bikeshed source](https://github.com/privacycg/requestStorageAccessForOrigin/blob/main/index.bs). diff --git a/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessForOrigin-insecure.sub.window.js b/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessFor-insecure.sub.window.js similarity index 56% rename from tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessForOrigin-insecure.sub.window.js rename to tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessFor-insecure.sub.window.js index 8cf72b85fa8..18d752f28ad 100644 --- a/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessForOrigin-insecure.sub.window.js +++ b/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessFor-insecure.sub.window.js @@ -28,82 +28,82 @@ queryParams.forEach((param) => { } }); -// TODO(crbug.com/1410556): when/if requestStorageAccessForOrigin is standardized, +// TODO(crbug.com/1410556): when/if requestStorageAccessFor is standardized, // we should consider upstreaming these helpers. -function RunRequestStorageAccessForOriginInDetachedFrame(site) { +function RunRequestStorageAccessForInDetachedFrame(site) { const nestedFrame = document.createElement('iframe'); document.body.append(nestedFrame); const inner_doc = nestedFrame.contentDocument; nestedFrame.remove(); - return inner_doc.requestStorageAccessForOrigin(site); + return inner_doc.requestStorageAccessFor(site); } -function RunRequestStorageAccessForOriginViaDomParser(site) { +function RunRequestStorageAccessForViaDomParser(site) { const parser = new DOMParser(); const doc = parser.parseFromString('', 'text/html'); - return doc.requestStorageAccessForOrigin(site); + return doc.requestStorageAccessFor(site); } // Common tests to run in all frames. test( () => { - assert_not_equals(document.requestStorageAccessForOrigin, undefined); + assert_not_equals(document.requestStorageAccessFor, undefined); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be supported on the document interface'); + '] document.requestStorageAccessFor() should be supported on the document interface'); if (topLevelDocument) { promise_test( t => { return promise_rejects_dom(t, 'NotAllowedError', - document.requestStorageAccessForOrigin('https://test.com'), - 'document.requestStorageAccessForOrigin() call without user gesture'); + document.requestStorageAccessFor('https://test.com'), + 'document.requestStorageAccessFor() call without user gesture'); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be rejected by default with no user gesture'); + '] document.requestStorageAccessFor() should be rejected by default with no user gesture'); promise_test(async t => { const description = - 'document.requestStorageAccessForOrigin() call in a detached frame'; + 'document.requestStorageAccessFor() call in a detached frame'; // Can't use promise_rejects_dom here because the exception is from the wrong global. - return RunRequestStorageAccessForOriginInDetachedFrame('https://foo.com') + return RunRequestStorageAccessForInDetachedFrame('https://foo.com') .then(t.unreached_func('Should have rejected: ' + description)) .catch((e) => { assert_equals(e.name, 'InvalidStateError', description); }); - }, '[non-fully-active] document.requestStorageAccessForOrigin() should not resolve when run in a detached frame'); + }, '[non-fully-active] document.requestStorageAccessFor() should not resolve when run in a detached frame'); promise_test(async t => { const description = - 'document.requestStorageAccessForOrigin() in a detached DOMParser result'; - return RunRequestStorageAccessForOriginViaDomParser('https://foo.com') + 'document.requestStorageAccessFor() in a detached DOMParser result'; + return RunRequestStorageAccessForViaDomParser('https://foo.com') .then(t.unreached_func('Should have rejected: ' + description)) .catch((e) => { assert_equals(e.name, 'InvalidStateError', description); }); - }, '[non-fully-active] document.requestStorageAccessForOrigin() should not resolve when run in a detached DOMParser document'); + }, '[non-fully-active] document.requestStorageAccessFor() should not resolve when run in a detached DOMParser document'); // Create a test with a single-child same-origin iframe. - // This will validate that calls to requestStorageAccessForOrigin are rejected + // This will validate that calls to requestStorageAccessFor are rejected // in non-top-level contexts. RunTestsInIFrame( - './resources/requestStorageAccessForOrigin-iframe.html?testCase=frame-on-insecure-page&rootdocument=false'); + './resources/requestStorageAccessFor-iframe.html?testCase=frame-on-insecure-page&rootdocument=false'); promise_test( async t => { await RunCallbackWithGesture( - () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessForOrigin(document.location.origin), 'document.requestStorageAccessForOrigin() call in insecure context')); + () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessFor(document.location.origin), 'document.requestStorageAccessFor() call in insecure context')); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be rejected when called in an insecure context'); + '] document.requestStorageAccessFor() should be rejected when called in an insecure context'); } else { promise_test( async t => { await RunCallbackWithGesture( - () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessForOrigin(document.location.origin), - 'document.requestStorageAccessForOrigin() call in a non-top-level context')); + () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessFor(document.location.origin), + 'document.requestStorageAccessFor() call in a non-top-level context')); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be rejected when called in an iframe'); + '] document.requestStorageAccessFor() should be rejected when called in an iframe'); } diff --git a/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessForOrigin.sub.https.window.js b/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessFor.sub.https.window.js similarity index 60% rename from tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessForOrigin.sub.https.window.js rename to tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessFor.sub.https.window.js index 6fff9e9189e..9e16740cd2a 100644 --- a/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessForOrigin.sub.https.window.js +++ b/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/requestStorageAccessFor.sub.https.window.js @@ -30,29 +30,29 @@ queryParams.forEach((param) => { const requestedOrigin = 'https://foo.com'; -// TODO(crbug.com/1351540): when/if requestStorageAccessForOrigin is standardized, +// TODO(crbug.com/1351540): when/if requestStorageAccessFor is standardized, // upstream with the Storage Access API helpers file. -function RunRequestStorageAccessForOriginInDetachedFrame(site) { +function RunRequestStorageAccessForInDetachedFrame(origin) { const nestedFrame = document.createElement('iframe'); document.body.append(nestedFrame); const inner_doc = nestedFrame.contentDocument; nestedFrame.remove(); - return inner_doc.requestStorageAccessForOrigin(site); + return inner_doc.requestStorageAccessFor(origin); } -function RunRequestStorageAccessForOriginViaDomParser(site) { +function RunRequestStorageAccessForViaDomParser(origin) { const parser = new DOMParser(); const doc = parser.parseFromString('', 'text/html'); - return doc.requestStorageAccessForOrigin(site); + return doc.requestStorageAccessFor(origin); } // Common tests to run in all frames. test( () => { - assert_not_equals(document.requestStorageAccessForOrigin, undefined); + assert_not_equals(document.requestStorageAccessFor, undefined); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be supported on the document interface'); + '] document.requestStorageAccessFor() should be supported on the document interface'); // Promise tests should all start with the feature in "prompt" state. promise_setup(async () => { @@ -64,52 +64,52 @@ promise_setup(async () => { promise_test( t => { return promise_rejects_js(t, TypeError, - document.requestStorageAccessForOrigin(), - 'document.requestStorageAccessForOrigin() call without origin argument'); + document.requestStorageAccessFor(), + 'document.requestStorageAccessFor() call without origin argument'); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be rejected when called with no argument'); + '] document.requestStorageAccessFor() should be rejected when called with no argument'); if (topLevelDocument) { promise_test( t => { return promise_rejects_dom(t, 'NotAllowedError', - document.requestStorageAccessForOrigin(requestedOrigin), - 'document.requestStorageAccessForOrigin() call without user gesture'); + document.requestStorageAccessFor(requestedOrigin), + 'document.requestStorageAccessFor() call without user gesture'); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be rejected by default with no user gesture'); + '] document.requestStorageAccessFor() should be rejected by default with no user gesture'); promise_test(async t => { const description = - 'document.requestStorageAccessForOrigin() call in a detached frame'; + 'document.requestStorageAccessFor() call in a detached frame'; // Can't use promise_rejects_dom here because the exception is from the wrong global. - return RunRequestStorageAccessForOriginInDetachedFrame(requestedOrigin) + return RunRequestStorageAccessForInDetachedFrame(requestedOrigin) .then(t.unreached_func('Should have rejected: ' + description)) .catch((e) => { assert_equals(e.name, 'InvalidStateError', description); }); - }, '[non-fully-active] document.requestStorageAccessForOrigin() should not resolve when run in a detached frame'); + }, '[non-fully-active] document.requestStorageAccessFor() should not resolve when run in a detached frame'); promise_test(async t => { const description = - 'document.requestStorageAccessForOrigin() in a detached DOMParser result'; - return RunRequestStorageAccessForOriginViaDomParser(requestedOrigin) + 'document.requestStorageAccessFor() in a detached DOMParser result'; + return RunRequestStorageAccessForViaDomParser(requestedOrigin) .then(t.unreached_func('Should have rejected: ' + description)) .catch((e) => { assert_equals(e.name, 'InvalidStateError', description); }); - }, '[non-fully-active] document.requestStorageAccessForOrigin() should not resolve when run in a detached DOMParser document'); + }, '[non-fully-active] document.requestStorageAccessFor() should not resolve when run in a detached DOMParser document'); promise_test( async t => { await test_driver.set_permission( { name: 'top-level-storage-access', requestedOrigin }, 'granted'); - await document.requestStorageAccessForOrigin(requestedOrigin); + await document.requestStorageAccessFor(requestedOrigin); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be resolved without a user gesture with an existing permission'); + '] document.requestStorageAccessFor() should be resolved without a user gesture with an existing permission'); promise_test( async t => { @@ -125,54 +125,54 @@ if (topLevelDocument) { 'granted'); await RunCallbackWithGesture(() => { - document.requestStorageAccessForOrigin(altOrigin).then(() => { + document.requestStorageAccessFor(altOrigin).then(() => { RunTestsInIFrame( 'https://{{hosts[alt][www]}}:{{ports[https][0]}}/top-level-storage-access-api/tentative/resources/requestStorageAccess-integration-iframe.https.html'); }); }); }, '[' + testPrefix + - '] document.requestStorageAccess() should be resolved without a user gesture after a successful requestStorageAccessForOrigin() call'); + '] document.requestStorageAccess() should be resolved without a user gesture after a successful requestStorageAccessFor() call'); // Create a test with a single-child same-origin iframe. - // This will validate that calls to requestStorageAccessForOrigin are rejected + // This will validate that calls to requestStorageAccessFor are rejected // in non-top-level contexts. RunTestsInIFrame( - './resources/requestStorageAccessForOrigin-iframe.https.html?testCase=same-origin-frame&rootdocument=false'); + './resources/requestStorageAccessFor-iframe.https.html?testCase=same-origin-frame&rootdocument=false'); promise_test( async t => { await RunCallbackWithGesture( - () => document.requestStorageAccessForOrigin(document.location.origin)); + () => document.requestStorageAccessFor(document.location.origin)); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be resolved when called properly with a user gesture and the same site'); + '] document.requestStorageAccessFor() should be resolved when called properly with a user gesture and the same site'); promise_test( async t => { await RunCallbackWithGesture( - () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessForOrigin('bogus-url'), - 'document.requestStorageAccessForOrigin() call with bogus URL')); + () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessFor('bogus-url'), + 'document.requestStorageAccessFor() call with bogus URL')); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be rejected when called with an invalid site'); + '] document.requestStorageAccessFor() should be rejected when called with an invalid site'); promise_test( async t => { await RunCallbackWithGesture( - () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessForOrigin('data:,Hello%2C%20World%21'), - 'document.requestStorageAccessForOrigin() call with data URL')); + () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessFor('data:,Hello%2C%20World%21'), + 'document.requestStorageAccessFor() call with data URL')); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be rejected when called with an opaque origin'); + '] document.requestStorageAccessFor() should be rejected when called with an opaque origin'); } else { promise_test( async t => { await RunCallbackWithGesture( - () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessForOrigin(document.location.origin), - 'document.requestStorageAccessForOrigin() call in a non-top-level context')); + () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessFor(document.location.origin), + 'document.requestStorageAccessFor() call in a non-top-level context')); }, '[' + testPrefix + - '] document.requestStorageAccessForOrigin() should be rejected when called in an iframe'); + '] document.requestStorageAccessFor() should be rejected when called in an iframe'); } diff --git a/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessForOrigin-iframe.https.html b/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessFor-iframe.html similarity index 77% rename from tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessForOrigin-iframe.https.html rename to tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessFor-iframe.html index db03b25e14d..050196dfdb9 100644 --- a/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessForOrigin-iframe.https.html +++ b/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessFor-iframe.html @@ -6,4 +6,4 @@
- + diff --git a/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessForOrigin-iframe.html b/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessFor-iframe.https.html similarity index 76% rename from tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessForOrigin-iframe.html rename to tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessFor-iframe.https.html index 43d16705c85..14da63b033c 100644 --- a/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessForOrigin-iframe.html +++ b/tests/wpt/web-platform-tests/top-level-storage-access-api/tentative/resources/requestStorageAccessFor-iframe.https.html @@ -6,4 +6,4 @@
- + diff --git a/tests/wpt/web-platform-tests/url/javascript-urls.window.js b/tests/wpt/web-platform-tests/url/javascript-urls.window.js new file mode 100644 index 00000000000..4f617beca17 --- /dev/null +++ b/tests/wpt/web-platform-tests/url/javascript-urls.window.js @@ -0,0 +1,63 @@ +// The results of this test are all over the map due to browsers behaving very differently for +// javascript: URLs. +// +// Chromium is pretty close execution-wise, but it parses javascript: URLs incorrectly. +// Gecko navigates to non-string return values of the result of executing a javascript: URL. +// WebKit executes javascript: URLs too early and has a harness error due to URL parsing. +// +// The expectations below should match the HTML and URL standards. +[ + { + "description": "javascript: URL that fails to parse due to invalid host", + "input": "javascript://test:test/%0aglobalThis.shouldNotExistA=1", + "property": "shouldNotExistA", + "expected": undefined + }, + { + "description": "javascript: URL that fails to parse due to invalid host and has a U+0009 in scheme", + "input": "java\x09script://test:test/%0aglobalThis.shouldNotExistB=1", + "property": "shouldNotExistB", + "expected": undefined + }, + { + "description": "javascript: URL without an opaque path", + "input": "javascript://host/1%0a//../0/;globalThis.shouldBeOne=1;/%0aglobalThis.shouldBeOne=2;/..///", + "property": "shouldBeOne", + "expected": 1 + }, + { + "description": "javascript: URL containing a JavaScript string split over path and query", + // Use ";undefined" to avoid returning a string. + "input": "javascript:globalThis.shouldBeAStringA = \"https://whatsoever.com/?a=b&c=5&x=y\";undefined", + "property": "shouldBeAStringA", + "expected": "https://whatsoever.com/?a=b&c=5&x=y" + }, + { + "description": "javascript: URL containing a JavaScript string split over path and query and has a U+000A in scheme", + // Use ";undefined" to avoid returning a string. + "input": "java\x0Ascript:globalThis.shouldBeAStringB = \"https://whatsoever.com/?a=b&c=5&x=y\";undefined", + "property": "shouldBeAStringB", + "expected": "https://whatsoever.com/?a=b&c=5&x=y" + } +].forEach(({ description, input, property, expected }) => { + // Use promise_test so the tests run in sequence. Needed for globalThis.verify below. + promise_test(t => { + const anchor = document.body.appendChild(document.createElement("a")); + t.add_cleanup(() => anchor.remove()); + anchor.href = input; + assert_equals(globalThis[property], undefined, "Property is undefined before the click"); + anchor.click(); + assert_equals(globalThis[property], undefined, "Property is undefined immediately after the click"); + + // Since we cannot reliably queue a task to run after the task queued as a result of the click() + // above, we do another click() with a new URL. + return new Promise(resolve => { + globalThis.verify = t.step_func(() => { + assert_equals(globalThis[property], expected, `Property is ${expected} once the navigation happened`); + resolve(); + }); + anchor.href = "javascript:globalThis.verify()"; + anchor.click(); + }); + }, description); +}); diff --git a/tests/wpt/web-platform-tests/url/resources/urltestdata.json b/tests/wpt/web-platform-tests/url/resources/urltestdata.json index eb414860f27..a3cf976534c 100644 --- a/tests/wpt/web-platform-tests/url/resources/urltestdata.json +++ b/tests/wpt/web-platform-tests/url/resources/urltestdata.json @@ -8923,5 +8923,426 @@ "input": "https://xn--/", "base": "about:blank", "failure": true + }, + "Non-special schemes that some implementations might incorrectly treat as special", + { + "input": "data://example.com:8080/pathname?search#hash", + "base": "about:blank", + "href": "data://example.com:8080/pathname?search#hash", + "origin": "null", + "protocol": "data:", + "username": "", + "password": "", + "host": "example.com:8080", + "hostname": "example.com", + "port": "8080", + "pathname": "/pathname", + "search": "?search", + "hash": "#hash" + }, + { + "input": "data:///test", + "base": "about:blank", + "href": "data:///test", + "origin": "null", + "protocol": "data:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/test", + "search": "", + "hash": "" + }, + { + "input": "data://test/a/../b", + "base": "about:blank", + "href": "data://test/b", + "origin": "null", + "protocol": "data:", + "username": "", + "password": "", + "host": "test", + "hostname": "test", + "port": "", + "pathname": "/b", + "search": "", + "hash": "" + }, + { + "input": "data://:443", + "base": "about:blank", + "failure": true + }, + { + "input": "data://test:test", + "base": "about:blank", + "failure": true + }, + { + "input": "data://[:1]", + "base": "about:blank", + "failure": true + }, + { + "input": "javascript://example.com:8080/pathname?search#hash", + "base": "about:blank", + "href": "javascript://example.com:8080/pathname?search#hash", + "origin": "null", + "protocol": "javascript:", + "username": "", + "password": "", + "host": "example.com:8080", + "hostname": "example.com", + "port": "8080", + "pathname": "/pathname", + "search": "?search", + "hash": "#hash" + }, + { + "input": "javascript:///test", + "base": "about:blank", + "href": "javascript:///test", + "origin": "null", + "protocol": "javascript:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/test", + "search": "", + "hash": "" + }, + { + "input": "javascript://test/a/../b", + "base": "about:blank", + "href": "javascript://test/b", + "origin": "null", + "protocol": "javascript:", + "username": "", + "password": "", + "host": "test", + "hostname": "test", + "port": "", + "pathname": "/b", + "search": "", + "hash": "" + }, + { + "input": "javascript://:443", + "base": "about:blank", + "failure": true + }, + { + "input": "javascript://test:test", + "base": "about:blank", + "failure": true + }, + { + "input": "javascript://[:1]", + "base": "about:blank", + "failure": true + }, + { + "input": "mailto://example.com:8080/pathname?search#hash", + "base": "about:blank", + "href": "mailto://example.com:8080/pathname?search#hash", + "origin": "null", + "protocol": "mailto:", + "username": "", + "password": "", + "host": "example.com:8080", + "hostname": "example.com", + "port": "8080", + "pathname": "/pathname", + "search": "?search", + "hash": "#hash" + }, + { + "input": "mailto:///test", + "base": "about:blank", + "href": "mailto:///test", + "origin": "null", + "protocol": "mailto:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/test", + "search": "", + "hash": "" + }, + { + "input": "mailto://test/a/../b", + "base": "about:blank", + "href": "mailto://test/b", + "origin": "null", + "protocol": "mailto:", + "username": "", + "password": "", + "host": "test", + "hostname": "test", + "port": "", + "pathname": "/b", + "search": "", + "hash": "" + }, + { + "input": "mailto://:443", + "base": "about:blank", + "failure": true + }, + { + "input": "mailto://test:test", + "base": "about:blank", + "failure": true + }, + { + "input": "mailto://[:1]", + "base": "about:blank", + "failure": true + }, + { + "input": "intent://example.com:8080/pathname?search#hash", + "base": "about:blank", + "href": "intent://example.com:8080/pathname?search#hash", + "origin": "null", + "protocol": "intent:", + "username": "", + "password": "", + "host": "example.com:8080", + "hostname": "example.com", + "port": "8080", + "pathname": "/pathname", + "search": "?search", + "hash": "#hash" + }, + { + "input": "intent:///test", + "base": "about:blank", + "href": "intent:///test", + "origin": "null", + "protocol": "intent:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/test", + "search": "", + "hash": "" + }, + { + "input": "intent://test/a/../b", + "base": "about:blank", + "href": "intent://test/b", + "origin": "null", + "protocol": "intent:", + "username": "", + "password": "", + "host": "test", + "hostname": "test", + "port": "", + "pathname": "/b", + "search": "", + "hash": "" + }, + { + "input": "intent://:443", + "base": "about:blank", + "failure": true + }, + { + "input": "intent://test:test", + "base": "about:blank", + "failure": true + }, + { + "input": "intent://[:1]", + "base": "about:blank", + "failure": true + }, + { + "input": "urn://example.com:8080/pathname?search#hash", + "base": "about:blank", + "href": "urn://example.com:8080/pathname?search#hash", + "origin": "null", + "protocol": "urn:", + "username": "", + "password": "", + "host": "example.com:8080", + "hostname": "example.com", + "port": "8080", + "pathname": "/pathname", + "search": "?search", + "hash": "#hash" + }, + { + "input": "urn:///test", + "base": "about:blank", + "href": "urn:///test", + "origin": "null", + "protocol": "urn:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/test", + "search": "", + "hash": "" + }, + { + "input": "urn://test/a/../b", + "base": "about:blank", + "href": "urn://test/b", + "origin": "null", + "protocol": "urn:", + "username": "", + "password": "", + "host": "test", + "hostname": "test", + "port": "", + "pathname": "/b", + "search": "", + "hash": "" + }, + { + "input": "urn://:443", + "base": "about:blank", + "failure": true + }, + { + "input": "urn://test:test", + "base": "about:blank", + "failure": true + }, + { + "input": "urn://[:1]", + "base": "about:blank", + "failure": true + }, + { + "input": "turn://example.com:8080/pathname?search#hash", + "base": "about:blank", + "href": "turn://example.com:8080/pathname?search#hash", + "origin": "null", + "protocol": "turn:", + "username": "", + "password": "", + "host": "example.com:8080", + "hostname": "example.com", + "port": "8080", + "pathname": "/pathname", + "search": "?search", + "hash": "#hash" + }, + { + "input": "turn:///test", + "base": "about:blank", + "href": "turn:///test", + "origin": "null", + "protocol": "turn:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/test", + "search": "", + "hash": "" + }, + { + "input": "turn://test/a/../b", + "base": "about:blank", + "href": "turn://test/b", + "origin": "null", + "protocol": "turn:", + "username": "", + "password": "", + "host": "test", + "hostname": "test", + "port": "", + "pathname": "/b", + "search": "", + "hash": "" + }, + { + "input": "turn://:443", + "base": "about:blank", + "failure": true + }, + { + "input": "turn://test:test", + "base": "about:blank", + "failure": true + }, + { + "input": "turn://[:1]", + "base": "about:blank", + "failure": true + }, + { + "input": "stun://example.com:8080/pathname?search#hash", + "base": "about:blank", + "href": "stun://example.com:8080/pathname?search#hash", + "origin": "null", + "protocol": "stun:", + "username": "", + "password": "", + "host": "example.com:8080", + "hostname": "example.com", + "port": "8080", + "pathname": "/pathname", + "search": "?search", + "hash": "#hash" + }, + { + "input": "stun:///test", + "base": "about:blank", + "href": "stun:///test", + "origin": "null", + "protocol": "stun:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/test", + "search": "", + "hash": "" + }, + { + "input": "stun://test/a/../b", + "base": "about:blank", + "href": "stun://test/b", + "origin": "null", + "protocol": "stun:", + "username": "", + "password": "", + "host": "test", + "hostname": "test", + "port": "", + "pathname": "/b", + "search": "", + "hash": "" + }, + { + "input": "stun://:443", + "base": "about:blank", + "failure": true + }, + { + "input": "stun://test:test", + "base": "about:blank", + "failure": true + }, + { + "input": "stun://[:1]", + "base": "about:blank", + "failure": true } ] diff --git a/tests/wpt/web-platform-tests/url/urlencoded-parser.any.js b/tests/wpt/web-platform-tests/url/urlencoded-parser.any.js index 46b932bb014..847465cb921 100644 --- a/tests/wpt/web-platform-tests/url/urlencoded-parser.any.js +++ b/tests/wpt/web-platform-tests/url/urlencoded-parser.any.js @@ -2,6 +2,7 @@ { "input": "test", "output": [["test", ""]] }, { "input": "\uFEFFtest=\uFEFF", "output": [["\uFEFFtest", "\uFEFF"]] }, { "input": "%EF%BB%BFtest=%EF%BB%BF", "output": [["\uFEFFtest", "\uFEFF"]] }, + { "input": "%EF%BF%BF=%EF%BF%BF", "output": [["\uFFFF", "\uFFFF"]] }, { "input": "%FE%FF", "output": [["\uFFFD\uFFFD", ""]] }, { "input": "%FF%FE", "output": [["\uFFFD\uFFFD", ""]] }, { "input": "†&†=x", "output": [["†", ""], ["†", "x"]] }, diff --git a/tests/wpt/web-platform-tests/web-animations/animation-model/keyframe-effects/keyframe-exceptions.html b/tests/wpt/web-platform-tests/web-animations/animation-model/keyframe-effects/keyframe-exceptions.html new file mode 100644 index 00000000000..4cb4be76a8a --- /dev/null +++ b/tests/wpt/web-platform-tests/web-animations/animation-model/keyframe-effects/keyframe-exceptions.html @@ -0,0 +1,39 @@ + + + + + + + + +Keyframes with invalid offsets + + diff --git a/tests/wpt/web-platform-tests/web-animations/interfaces/Animation/style-change-events.html b/tests/wpt/web-platform-tests/web-animations/interfaces/Animation/style-change-events.html index b41f748720a..0ec21657e3c 100644 --- a/tests/wpt/web-platform-tests/web-animations/interfaces/Animation/style-change-events.html +++ b/tests/wpt/web-platform-tests/web-animations/interfaces/Animation/style-change-events.html @@ -164,6 +164,11 @@ const tests = { }), playState: UsePropertyTest(animation => animation.playState), pending: UsePropertyTest(animation => animation.pending), + // Strictly speaking, rangeStart and rangeEnd can change whether the effect + // is active, but only if the animation has a view timeline. Otherwise, it has + // no effect. + rangeStart: UsePropertyTest(animation => animation.rangeStart), + rangeEnd: UsePropertyTest(animation => animation.rangeEnd), replaceState: UsePropertyTest(animation => animation.replaceState), ready: UsePropertyTest(animation => animation.ready), finished: UsePropertyTest(animation => { diff --git a/tests/wpt/web-platform-tests/webcodecs/per-frame-qp-encoding.https.any.js b/tests/wpt/web-platform-tests/webcodecs/per-frame-qp-encoding.https.any.js new file mode 100644 index 00000000000..3207fa8356c --- /dev/null +++ b/tests/wpt/web-platform-tests/webcodecs/per-frame-qp-encoding.https.any.js @@ -0,0 +1,134 @@ +// META: global=window,dedicatedworker +// META: script=/webcodecs/video-encoder-utils.js +// META: variant=?av1 +// META: variant=?vp9_p0 +// META: variant=?vp9_p2 + +function get_config() { + const config = { + '?av1': {codec: 'av01.0.04M.08'}, + '?vp8': {codec: 'vp8'}, + '?vp9_p0': {codec: 'vp09.00.10.08'}, + '?vp9_p2': {codec: 'vp09.02.10.10'}, + '?h264': {codec: 'avc1.42001E', avc: {format: 'annexb'}} + }[location.search]; + config.width = 320; + config.height = 200; + config.bitrate = 1000000; + config.bitrateMode = 'quantizer'; + config.framerate = 30; + return config; +} + +function get_qp_range() { + switch (location.search) { + case '?av1': + return {min: 1, max: 63}; + case '?vp9_p0': + return {min: 1, max: 63}; + case '?vp9_p2': + return {min: 1, max: 63}; + } + return null; +} + +function set_qp(options, value) { + switch (location.search) { + case '?av1': + options.av1 = {quantizer: value}; + return; + case '?vp9_p0': + options.vp9 = {quantizer: value}; + return; + case '?vp9_p2': + options.vp9 = {quantizer: value}; + return; + } +} + +async function per_frame_qp_test(t, encoder_config, qp_range, validate_result) { + const w = encoder_config.width; + const h = encoder_config.height; + await checkEncoderSupport(t, encoder_config); + + let frames_to_encode = 24; + let frames_decoded = 0; + let frames_encoded = 0; + let chunks = []; + let corrupted_frames = []; + + const encoder_init = { + output(chunk, metadata) { + frames_encoded++; + chunks.push(chunk); + }, + error(e) { + assert_unreached(e.message); + } + }; + + let encoder = new VideoEncoder(encoder_init); + encoder.configure(encoder_config); + + let qp = qp_range.min; + for (let i = 0; i < frames_to_encode; i++) { + let frame = createDottedFrame(w, h, i); + if (qp < qp_range.max) { + qp++; + } else { + qp = qp_range.min; + } + let encode_options = {keyFrame: false}; + set_qp(encode_options, qp); + encoder.encode(frame, encode_options); + frame.close(); + } + await encoder.flush(); + + let decoder = new VideoDecoder({ + output(frame) { + frames_decoded++; + // Check that we have intended number of dots and no more. + // Completely black frame shouldn't pass the test. + if (validate_result && !validateBlackDots(frame, frame.timestamp) || + validateBlackDots(frame, frame.timestamp + 1)) { + corrupted_frames.push(frame.timestamp) + } + frame.close(); + }, + error(e) { + assert_unreached(e.message); + } + }); + + let decoder_config = { + codec: encoder_config.codec, + codedWidth: w, + codedHeight: h, + }; + decoder.configure(decoder_config); + + for (let chunk of chunks) { + decoder.decode(chunk); + } + await decoder.flush(); + + encoder.close(); + decoder.close(); + assert_equals(frames_encoded, frames_to_encode); + assert_equals(chunks.length, frames_to_encode); + assert_equals(frames_decoded, frames_to_encode); + assert_equals( + corrupted_frames.length, 0, `corrupted_frames: ${corrupted_frames}`); +} + +promise_test(async t => { + let config = get_config(); + let range = get_qp_range(); + return per_frame_qp_test(t, config, range, false); +}, 'Frame QP encoding, full range'); + +promise_test(async t => { + let config = get_config(); + return per_frame_qp_test(t, config, {min: 1, max: 20}, true); +}, 'Frame QP encoding, good range with validation'); diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/__init__.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/__init__.py index cd0ad7a2826..066c34f3f28 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/__init__.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/__init__.py @@ -1,11 +1,7 @@ -from base64 import encodebytes - from webdriver.bidi.modules.script import ContextTarget -from tests.support.image import png_dimensions - async def viewport_dimensions(bidi_session, context): - """Get the dimensions of the viewport containing context. + """Get the dimensions of the context's viewport. :param bidi_session: BiDiSession :param context: Browsing context ID @@ -23,72 +19,3 @@ async def viewport_dimensions(bidi_session, context): target=ContextTarget(context["context"]), await_promise=False) return tuple(item["value"] for item in result["value"]) - - -class ImageDifference: - """Summary of the pixel-level differences between two images. - - :param total_pixels: The total number of pixel differences between the images - :param max_difference: The maximum difference between any corresponding color channels across - all pixels of the image. - """ - - def __init__(self, total_pixels, max_difference): - self.total_pixels = total_pixels - self.max_difference = max_difference - - def equal(self): - return self.total_pixels == 0 - - -async def compare_png_data(bidi_session, - url, - img1: bytes, - img2: bytes) -> ImageDifference: - """Calculate difference statistics between two PNG images. - - :param bidi_session: BidiSession - :param url: fixture to construct a URL string given a path - :param img1: Bytes of first PNG image - :param img2: Bytes of second PNG image - :returns: ImageDifference representing the total number of different pixels, - and maximum per-channel difference between the images. - """ - if img1 == img2: - return ImageDifference(0, 0) - - width, height = png_dimensions(img1) - assert (width, height) == png_dimensions(img2) - - context = await bidi_session.browsing_context.create(type_hint="tab") - await bidi_session.browsing_context.navigate( - context=context["context"], - url=url("/webdriver/tests/support/html/render.html"), - wait="complete") - result = await bidi_session.script.call_function( - function_declaration="""(img1, img2, width, height) => { - return compare(img1, img2, width, height) - }""", - target=ContextTarget(context["context"]), - arguments=[{"type": "string", - "value": encodebytes(img1).decode()}, - {"type": "string", - "value": encodebytes(img2).decode()}, - {"type": "number", - "value": width}, - {"type": "number", - "value": height}], - await_promise=True) - await bidi_session.browsing_context.close(context=context["context"]) - assert result["type"] == "object" - assert set(item[0] for item in result["value"]) == {"totalPixels", "maxDifference"} - for item in result["value"]: - assert len(item) == 2 - assert item[1]["type"] == "number" - if item[0] == "totalPixels": - total_pixels = item[1]["value"] - elif item[0] == "maxDifference": - max_difference = item[1]["value"] - else: - raise Exception(f"Unexpected object key ${item[0]}") - return ImageDifference(total_pixels, max_difference) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/capture_screenshot.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/capture_screenshot.py index f406698096a..f6e4c34d8d1 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/capture_screenshot.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/capture_screenshot.py @@ -2,11 +2,11 @@ import pytest from tests.support.image import png_dimensions -from . import compare_png_data, viewport_dimensions +from . import viewport_dimensions @pytest.mark.asyncio -async def test_capture(bidi_session, url, top_context, inline): +async def test_capture(bidi_session, url, top_context, inline, compare_png_bidi): expected_size = await viewport_dimensions(bidi_session, top_context) await bidi_session.browsing_context.navigate( @@ -22,10 +22,7 @@ async def test_capture(bidi_session, url, top_context, inline): data = await bidi_session.browsing_context.capture_screenshot( context=top_context["context"]) - comparison = await compare_png_data(bidi_session, - url, - reference_data, - data) + comparison = await compare_png_bidi(data, reference_data) assert not comparison.equal() # Take a second screenshot that should be identical to validate that @@ -36,8 +33,5 @@ async def test_capture(bidi_session, url, top_context, inline): new_data = await bidi_session.browsing_context.capture_screenshot( context=top_context["context"]) - comparison = await compare_png_data(bidi_session, - url, - data, - new_data) + comparison = await compare_png_bidi(new_data, data) assert comparison.equal() diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/frame.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/frame.py index 960998044cd..72786ba04d7 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/frame.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/capture_screenshot/frame.py @@ -7,7 +7,7 @@ from tests.support.screenshot import (DEFAULT_CONTENT, OUTER_IFRAME_STYLE, INNER_IFRAME_STYLE) -from . import compare_png_data, viewport_dimensions +from . import viewport_dimensions @pytest.mark.asyncio @@ -33,7 +33,7 @@ async def test_iframe(bidi_session, top_context, inline, iframe): @pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) @pytest.mark.asyncio -async def test_context_origin(bidi_session, url, top_context, inline, iframe, domain): +async def test_context_origin(bidi_session, top_context, inline, iframe, compare_png_bidi, domain): expected_size = await viewport_dimensions(bidi_session, top_context) initial_url = inline(f"{REFERENCE_STYLE}{REFERENCE_CONTENT}") @@ -52,9 +52,6 @@ async def test_context_origin(bidi_session, url, top_context, inline, iframe, do wait="complete") data = await bidi_session.browsing_context.capture_screenshot(context=top_context["context"]) - comparison = await compare_png_data(bidi_session, - url, - reference_data, - data) + comparison = await compare_png_bidi(data, reference_data) assert comparison.equal() diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/__init__.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/__init__.py index d34fef9869a..e69de29bb2d 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/__init__.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/__init__.py @@ -1,6 +0,0 @@ -import pytest - - -def assert_pdf(data): - assert data.startswith(b"%PDF-"), "Decoded data starts with the PDF signature" - assert data.endswith(b"%%EOF\n"), "Decoded data ends with the EOF flag" diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/background.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/background.py new file mode 100644 index 00000000000..b527aef3827 --- /dev/null +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/background.py @@ -0,0 +1,56 @@ +import base64 +import pytest + +from tests.support.image import px_to_cm +from tests.support.pdf import assert_pdf + + +pytestmark = pytest.mark.asyncio + + +INLINE_BACKGROUND_RENDERING_TEST_CONTENT = """ + +""" + +BLACK_DOT_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2NgYGD4DwABBAEAwS2OUAAAAABJRU5ErkJggg==" +WHITE_DOT_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2P4DwQACfsD/Z8fLAAAAAAASUVORK5CYII=" + + +@pytest.mark.parametrize( + "print_with_background, expected_image", + [ + (None, WHITE_DOT_PNG), + (True, BLACK_DOT_PNG), + (False, WHITE_DOT_PNG), + ], +) +async def test_background( + bidi_session, + top_context, + inline, + compare_png_bidi, + render_pdf_to_png_bidi, + print_with_background, + expected_image, +): + page = inline(INLINE_BACKGROUND_RENDERING_TEST_CONTENT) + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=page, wait="complete" + ) + + print_value = await bidi_session.browsing_context.print( + context=top_context["context"], + background=print_with_background, + margin={"top": 0, "bottom": 0, "right": 0, "left": 0}, + page={"width": px_to_cm(1), "height": px_to_cm(1)}, + ) + + assert_pdf(print_value) + + png = await render_pdf_to_png_bidi(print_value) + comparison = await compare_png_bidi(png, base64.b64decode(expected_image)) + assert comparison.equal() diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/context.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/context.py index a73bfee9621..f8074b71b43 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/context.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/context.py @@ -1,14 +1,9 @@ -from base64 import decodebytes - import pytest -from . import assert_pdf -from ... import recursive_compare - pytestmark = pytest.mark.asyncio -async def test_context(bidi_session, top_context, inline, get_pdf_content): +async def test_context(bidi_session, top_context, inline, assert_pdf_content): text = "Test" url = inline(text) await bidi_session.browsing_context.navigate( @@ -16,11 +11,51 @@ async def test_context(bidi_session, top_context, inline, get_pdf_content): ) value = await bidi_session.browsing_context.print(context=top_context["context"]) - pdf = decodebytes(value.encode()) - assert_pdf(pdf) + await assert_pdf_content(value, [{"type": "string", "value": text}]) - pdf_content = await get_pdf_content(value) - recursive_compare( - {"type": "array", "value": [{"type": "string", "value": text}]}, pdf_content + +async def test_page_with_iframe( + bidi_session, top_context, inline, iframe, assert_pdf_content +): + text = "Test" + iframe_content = "Iframe" + url = inline(f"{text}
{iframe(iframe_content)}") + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=url, wait="complete" ) + + whole_page_value = await bidi_session.browsing_context.print( + context=top_context["context"] + ) + + await assert_pdf_content( + whole_page_value, [{"type": "string", "value": text + iframe_content}] + ) + + contexts = await bidi_session.browsing_context.get_tree(root=top_context["context"]) + frame_context = contexts[0]["children"][0] + + frame_value = await bidi_session.browsing_context.print( + context=frame_context["context"] + ) + + await assert_pdf_content(frame_value, [{"type": "string", "value": iframe_content}]) + + +@pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) +async def test_context_origin( + bidi_session, top_context, inline, iframe, assert_pdf_content, domain +): + iframe_content = "Iframe" + url = inline(f"{iframe(iframe_content, domain=domain)}") + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=url, wait="complete" + ) + + contexts = await bidi_session.browsing_context.get_tree(root=top_context["context"]) + frame_context = contexts[0]["children"][0] + + value = await bidi_session.browsing_context.print(context=frame_context["context"]) + + await assert_pdf_content(value, [{"type": "string", "value": iframe_content}]) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/margin.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/margin.py new file mode 100644 index 00000000000..8ca1ef7077f --- /dev/null +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/margin.py @@ -0,0 +1,148 @@ +# META: timeout=long +import pytest + +pytestmark = pytest.mark.asyncio + + +def get_content(css=""): + return f""" +
+ + """ + + +@pytest.mark.parametrize( + "margin, reference_css, css", + [ + ( + {"top": 2.54}, + "margin-top: 1.54cm;", + "", + ), + ( + {"left": 2.54}, + "margin-left: 1.54cm;", + "", + ), + ( + {"right": 2.54}, + "margin-right: 1.54cm;", + "", + ), + ( + {"bottom": 2.54}, + "height: 24.4cm;", + "height: 26.94cm;", + ), + ], + ids=[ + "top", + "left", + "right", + "bottom", + ], +) +async def test_margin_default( + bidi_session, + top_context, + inline, + assert_pdf_image, + margin, + reference_css, + css, +): + default_content_page = inline(get_content(css)) + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=default_content_page, wait="complete" + ) + value_with_margin = await bidi_session.browsing_context.print( + context=top_context["context"], + shrink_to_fit=False, + background=True, + margin=margin, + ) + + # Compare a page with default margin (1.0cm) + css margin + # with a page with extended print margin. + await assert_pdf_image(value_with_margin, get_content(reference_css), True) + + +@pytest.mark.parametrize( + "margin", + [ + {"top": 27.94}, + {"left": 21.59}, + {"right": 21.59}, + {"bottom": 27.94}, + {"top": 27.94, "left": 21.59, "right": 21.59, "bottom": 27.94}, + ], + ids=[ + "top", + "left", + "right", + "bottom", + "all", + ], +) +async def test_margin_same_as_page_dimension( + bidi_session, + top_context, + inline, + assert_pdf_content, + margin, +): + page = inline("Text") + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=page, wait="complete" + ) + print_value = await bidi_session.browsing_context.print( + context=top_context["context"], + shrink_to_fit=False, + margin=margin, + ) + + # Check that content is out of page dimensions. + await assert_pdf_content(print_value, [{"type": "string", "value": ""}]) + + +@pytest.mark.parametrize( + "margin", + [ + {}, + {"top": 0, "left": 0, "right": 0, "bottom": 0}, + {"top": 2, "left": 2, "right": 2, "bottom": 2} + ], + ids=[ + "default", + "0", + "2" + ], +) +async def test_margin_does_not_affect_page_size( + bidi_session, + top_context, + inline, + assert_pdf_dimensions, + margin +): + url = inline("") + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=url, wait="complete" + ) + value = await bidi_session.browsing_context.print( + context=top_context["context"], + margin=margin + ) + + # Check that margins don't affect page dimencions and equal in this case defaults. + await assert_pdf_dimensions(value, {"width": 21.59, "height": 27.94}) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/orientation.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/orientation.py new file mode 100644 index 00000000000..4dee803bc8b --- /dev/null +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/orientation.py @@ -0,0 +1,43 @@ +import pytest + +from tests.support.image import png_dimensions +from tests.support.pdf import assert_pdf + + +pytestmark = pytest.mark.asyncio + + +@pytest.mark.parametrize( + "orientation_value, is_portrait", + [ + (None, True), + ("portrait", True), + ("landscape", False), + ], + ids=[ + "default", + "portrait", + "landscape", + ], +) +async def test_orientation( + bidi_session, + top_context, + inline, + render_pdf_to_png_bidi, + orientation_value, + is_portrait, +): + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=inline(""), wait="complete" + ) + print_value = await bidi_session.browsing_context.print( + context=top_context["context"], orientation=orientation_value + ) + + assert_pdf(print_value) + + png = await render_pdf_to_png_bidi(print_value) + width, height = png_dimensions(png) + + assert (width < height) == is_portrait diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/page.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/page.py new file mode 100644 index 00000000000..dd1fbcb9ae7 --- /dev/null +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/page.py @@ -0,0 +1,31 @@ +import pytest + +pytestmark = pytest.mark.asyncio + + +@pytest.mark.parametrize( + "page, expected_dimensions", + [ + (None, {"width": 21.59, "height": 27.94}), + ({}, {"width": 21.59, "height": 27.94}), + ({"width": 4.5}, {"width": 4.5, "height": 27.94}), + ({"height": 23}, {"width": 21.59, "height": 23}), + ({"width": 4.5, "height": 12}, {"width": 4.5, "height": 12}), + ], +) +async def test_page( + bidi_session, + top_context, + inline, + assert_pdf_dimensions, + page, + expected_dimensions, +): + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=inline(""), wait="complete" + ) + value = await bidi_session.browsing_context.print( + context=top_context["context"], page=page, orientation="portrait" + ) + + await assert_pdf_dimensions(value, expected_dimensions) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/page_ranges.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/page_ranges.py index 9e466883b0a..64843d34962 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/page_ranges.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/page_ranges.py @@ -1,11 +1,6 @@ # META: timeout=long -from base64 import decodebytes - import pytest -from . import assert_pdf -from ... import recursive_compare - pytestmark = pytest.mark.asyncio @@ -106,7 +101,7 @@ pytestmark = pytest.mark.asyncio ], ) async def test_page_ranges_document( - bidi_session, inline, top_context, get_pdf_content, ranges, expected + bidi_session, inline, top_context, assert_pdf_content, ranges, expected ): url = inline( """ @@ -132,9 +127,5 @@ div {page-break-after: always} value = await bidi_session.browsing_context.print( context=top_context["context"], page_ranges=ranges ) - pdf = decodebytes(value.encode()) - assert_pdf(pdf) - - pdf_content = await get_pdf_content(value) - recursive_compare({"type": "array", "value": expected}, pdf_content) + await assert_pdf_content(value, expected) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/scale.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/scale.py new file mode 100644 index 00000000000..bffc09af67c --- /dev/null +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/scale.py @@ -0,0 +1,57 @@ +import pytest + +pytestmark = pytest.mark.asyncio + + +def get_content(css=""): + return f""" +
+ + """ + + +@pytest.mark.parametrize( + "scale, reference_css", + [ + (None, "width: 100px; height: 100px;"), + (2, "width: 200px; height: 200px;"), + (0.5, "width: 50px; height: 50px;"), + ], + ids=["default", "twice", "half"], +) +async def test_scale( + bidi_session, + top_context, + inline, + assert_pdf_image, + scale, + reference_css, +): + not_scaled_content = get_content("width: 100px; height: 100px;") + default_content_page = inline(not_scaled_content) + + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=default_content_page, wait="complete" + ) + + scaled_print_value = await bidi_session.browsing_context.print( + context=top_context["context"], + shrink_to_fit=False, + scale=scale, + background=True, + ) + + # Check that pdf scaled with print command is equal pdf of scaled with css content. + await assert_pdf_image(scaled_print_value, get_content(reference_css), True) + # If scale is not None, check that pdf scaled with print command is not equal pdf with not scaled content. + if scale is not None: + await assert_pdf_image(scaled_print_value, not_scaled_content, False) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/shrink_to_fit.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/shrink_to_fit.py new file mode 100644 index 00000000000..db355280deb --- /dev/null +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/browsing_context/print/shrink_to_fit.py @@ -0,0 +1,50 @@ +import pytest + +pytestmark = pytest.mark.asyncio + + +@pytest.mark.parametrize( + "shrink_to_fit, pages_content", + [ + (None, [{"type": "string", "value": "Block 1Block 2Block 3Block 4"}]), + (True, [{"type": "string", "value": "Block 1Block 2Block 3Block 4"}]), + ( + False, + [ + {"type": "string", "value": "Block 1Block 2Block 3"}, + {"type": "string", "value": "Block 4"}, + ], + ), + ], + ids=["default", "True", "False"], +) +async def test_shrink_to_fit( + bidi_session, + top_context, + inline, + assert_pdf_content, + shrink_to_fit, + pages_content, +): + url = inline( + """ + +
Block 1
+
Block 2
+
Block 3
+
Block 4
+ """ + ) + await bidi_session.browsing_context.navigate( + context=top_context["context"], url=url, wait="complete" + ) + value = await bidi_session.browsing_context.print( + context=top_context["context"], shrink_to_fit=shrink_to_fit + ) + + await assert_pdf_content(value, pages_content) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/network/conftest.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/network/conftest.py index 3dd80f2896d..e6ca1f999b0 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/network/conftest.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/network/conftest.py @@ -1,6 +1,7 @@ import json import pytest +import pytest_asyncio from webdriver.bidi.modules.script import ContextTarget @@ -37,7 +38,7 @@ def fetch(bidi_session, top_context): return fetch -@pytest.fixture +@pytest_asyncio.fixture async def setup_network_test( bidi_session, subscribe_events, wait_for_event, top_context, url ): diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/__init__.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/__init__.py index 51fc26ce56b..de443c45780 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/__init__.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/__init__.py @@ -7,6 +7,18 @@ def assert_handle(obj: Mapping[str, Any], should_contain_handle: bool) -> None: if should_contain_handle: assert "handle" in obj, f"Result should contain `handle`. Actual: {obj}" assert isinstance(obj["handle"], str), f"`handle` should be a string, but was {type(obj['handle'])}" + + # Recursively check that handle is not found in any of the nested values. + if "value" in obj: + value = obj["value"] + if type(value) is list: + for v in value: + assert_handle(v, False) + + if type(value) is dict: + for v in value.values(): + assert_handle(v, False) + else: assert "handle" not in obj, f"Result should not contain `handle`. Actual: {obj}" diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/call_function/result_ownership.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/call_function/result_ownership.py index 01c5a28b62b..84b8f776b45 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/call_function/result_ownership.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/call_function/result_ownership.py @@ -52,7 +52,7 @@ async def test_rejected_promise(bidi_session, top_context, result_ownership, sho [("root", True), ("none", False), (None, False)]) async def test_return_value(bidi_session, top_context, await_promise, result_ownership, should_contain_handle): result = await bidi_session.script.call_function( - function_declaration="async function(){return {a:1}}", + function_declaration="async function(){return {a: {b:1}}}", await_promise=await_promise, result_ownership=result_ownership, target=ContextTarget(top_context["context"])) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/conftest.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/conftest.py index 09eea7b7d59..6423f9778e0 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/conftest.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/conftest.py @@ -1,4 +1,5 @@ import pytest +import pytest_asyncio from typing import Any, List, Mapping from webdriver.bidi.modules.script import ContextTarget, OwnershipModel @@ -32,7 +33,7 @@ def call_function(bidi_session, top_context): return call_function -@pytest.fixture +@pytest_asyncio.fixture async def default_realm(bidi_session, top_context): realms = await bidi_session.script.get_realms(context=top_context["context"]) return realms[0]["realm"] diff --git a/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/evaluate/result_ownership.py b/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/evaluate/result_ownership.py index 77e537bfe04..ab018699eb9 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/evaluate/result_ownership.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/bidi/script/evaluate/result_ownership.py @@ -52,7 +52,7 @@ async def test_rejected_promise(bidi_session, top_context, result_ownership, sho [("root", True), ("none", False), (None, False)]) async def test_return_value(bidi_session, top_context, await_promise, result_ownership, should_contain_handle): result = await bidi_session.script.evaluate( - expression="Promise.resolve({a:1})", + expression="Promise.resolve({a: {b:1}})", await_promise=await_promise, result_ownership=result_ownership, target=ContextTarget(top_context["context"])) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/print/__init__.py b/tests/wpt/web-platform-tests/webdriver/tests/print/__init__.py index e69de29bb2d..eb9a890cc45 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/print/__init__.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/print/__init__.py @@ -0,0 +1,21 @@ +def do_print(session, options={}): + params = {} + + if options.get("background", None) is not None: + params["background"] = options["background"] + if options.get("margin", None) is not None: + params["margin"] = options["margin"] + if options.get("orientation") is not None: + params["orientation"] = options["orientation"] + if options.get("page") is not None: + params["page"] = options["page"] + if options.get("pageRanges") is not None: + params["pageRanges"] = options["pageRanges"] + if options.get("scale") is not None: + params["scale"] = options["scale"] + if options.get("shrinkToFit") is not None: + params["shrinkToFit"] = options["shrinkToFit"] + + return session.transport.send( + "POST", "session/{session_id}/print".format(**vars(session)), params + ) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/print/background.py b/tests/wpt/web-platform-tests/webdriver/tests/print/background.py new file mode 100644 index 00000000000..22b392db2e3 --- /dev/null +++ b/tests/wpt/web-platform-tests/webdriver/tests/print/background.py @@ -0,0 +1,59 @@ +import base64 + +import pytest + +from tests.support.asserts import assert_success +from tests.support.image import px_to_cm +from tests.support.pdf import assert_pdf + +from . import do_print + + +INLINE_BACKGROUND_RENDERING_TEST_CONTENT = """ + +""" + +BLACK_DOT_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2NgYGD4DwABBAEAwS2OUAAAAABJRU5ErkJggg==" +WHITE_DOT_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2P4DwQACfsD/Z8fLAAAAAAASUVORK5CYII=" + + +@pytest.mark.parametrize( + "print_with_background, expected_image", + [ + (None, WHITE_DOT_PNG), + (True, BLACK_DOT_PNG), + (False, WHITE_DOT_PNG), + ], +) +def test_background( + session, + inline, + compare_png_http, + render_pdf_to_png_http, + print_with_background, + expected_image, +): + session.url = inline(INLINE_BACKGROUND_RENDERING_TEST_CONTENT) + + print_result = do_print( + session, + { + "background": print_with_background, + "margin": {"top": 0, "bottom": 0, "right": 0, "left": 0}, + "page": {"width": px_to_cm(1), "height": px_to_cm(1)}, + }, + ) + print_value = assert_success(print_result) + assert_pdf(print_value) + + png = render_pdf_to_png_http( + print_value + ) + comparison = compare_png_http( + png, base64.b64decode(expected_image) + ) + assert comparison.equal() diff --git a/tests/wpt/web-platform-tests/webdriver/tests/print/printcmd.py b/tests/wpt/web-platform-tests/webdriver/tests/print/printcmd.py index 415786675c5..f3fe50bd92d 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/print/printcmd.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/print/printcmd.py @@ -4,17 +4,9 @@ from base64 import decodebytes import pytest from tests.support.asserts import assert_error, assert_success +from tests.support.pdf import assert_pdf - -def do_print(session, options): - return session.transport.send( - "POST", "session/{session_id}/print".format(**vars(session)), - options) - - -def assert_pdf(data): - assert data.startswith(b"%PDF-"), "Decoded data starts with the PDF signature" - assert data.endswith(b"%%EOF\n"), "Decoded data ends with the EOF flag" +from . import do_print def test_no_top_browsing_context(session, closed_window): @@ -25,8 +17,7 @@ def test_no_top_browsing_context(session, closed_window): def test_no_browsing_context(session, closed_frame): response = do_print(session, {}) value = assert_success(response) - pdf = decodebytes(value.encode()) - assert_pdf(pdf) + assert_pdf(value) def test_html_document(session, inline): @@ -38,9 +29,8 @@ def test_html_document(session, inline): "shrinkToFit": False }) value = assert_success(response) - pdf = decodebytes(value.encode()) # TODO: Test that the output is reasonable - assert_pdf(pdf) + assert_pdf(value) def test_large_html_document(session, inline): @@ -76,7 +66,7 @@ def test_large_html_document(session, inline): # than 500kb would cause an error. If the resulting PDF is smaller than that # it could pass incorrectly. assert len(pdf) > 500000 - assert_pdf(pdf) + assert_pdf(value) @pytest.mark.parametrize("ranges,expected", [ @@ -90,7 +80,7 @@ def test_large_html_document(session, inline): (["-5", "2-"], ["Page 1", "Page 2", "Page 3", "Page 4", "Page 5", "Page 6", "Page 7", "Page 8", "Page 9", "Page 10"]), ([], ["Page 1", "Page 2", "Page 3", "Page 4", "Page 5", "Page 6", "Page 7", "Page 8", "Page 9", "Page 10"]), ]) -def test_page_ranges_document(session, inline, load_pdf_document, ranges, expected): +def test_page_ranges_document(session, inline, load_pdf_http, ranges, expected): session.url = inline("""