Refactor and split up with `variant` all encoding tests

Fixes #11015.
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-decode-cseucpkdfmtjapanese.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-decode-cseucpkdfmtjapanese.html
index 5d0e4cb..8448bf4 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-decode-cseucpkdfmtjapanese.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-decode-cseucpkdfmtjapanese.html
@@ -4,8 +4,23 @@
 <meta charset="utf-8"/>
 <title>cseucpkdfmtjapanese decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-jp">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'cseucpkdfmtjapanese' as for a document labeled 'euc-jp'.">
@@ -15,51 +30,15 @@
 <script src="jis0208_index.js"></script>
 <script src="jis0212_index.js"></script>
 <script src="eucjp-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(eucjpDecoder);">
 
 <iframe src="eucjp_chars-cseucpkdfmtjapanese.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-    return frameRef.contentWindow
-        ? frameRef.contentWindow.document
-        : frameRef.contentDocument;
-}
-
-function showNodes() {
-    var iframe = iframeRef(document.getElementById("scrwin"));
-    nodes = iframe.querySelectorAll("span");
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i] = async_test(
-            "U+" +
-                nodes[i].dataset.cp +
-                " " +
-                String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-                " " +
-                eucjpDecoder(nodes[i].dataset.bytes) +
-                " " +
-                nodes[i].dataset.bytes
-        );
-    }
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i].step(function() {
-            assert_equals(
-                nodes[i].textContent,
-                eucjpDecoder(nodes[i].dataset.bytes)
-            );
-        });
-        tests[i].done();
-    }
-}
-</script>
 </body>
 </html>
 
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-decode-x-euc-jp.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-decode-x-euc-jp.html
index 6b99117..b66bb2d 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-decode-x-euc-jp.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-decode-x-euc-jp.html
@@ -4,8 +4,23 @@
 <meta charset="utf-8"/>
 <title>x-euc-jp decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-jp">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'x-euc-jp' as for a document labeled 'euc-jp'.">
@@ -15,51 +30,15 @@
 <script src="jis0208_index.js"></script>
 <script src="jis0212_index.js"></script>
 <script src="eucjp-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(eucjpDecoder);">
 
 <iframe src="eucjp_chars-x-euc-jp.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-    return frameRef.contentWindow
-        ? frameRef.contentWindow.document
-        : frameRef.contentDocument;
-}
-
-function showNodes() {
-    var iframe = iframeRef(document.getElementById("scrwin"));
-    nodes = iframe.querySelectorAll("span");
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i] = async_test(
-            "U+" +
-                nodes[i].dataset.cp +
-                " " +
-                String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-                " " +
-                eucjpDecoder(nodes[i].dataset.bytes) +
-                " " +
-                nodes[i].dataset.bytes
-        );
-    }
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i].step(function() {
-            assert_equals(
-                nodes[i].textContent,
-                eucjpDecoder(nodes[i].dataset.bytes)
-            );
-        });
-        tests[i].done();
-    }
-}
-</script>
 </body>
 </html>
 
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-decode.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-decode.html
index ed05e31..0889856 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-decode.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-decode.html
@@ -4,8 +4,23 @@
 <meta charset="utf-8"/>
 <title>EUC-JP decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-jp">
 <meta name="assert" content="The browser decodes all characters as expected from a file generated by encoding all pointers in the euc-jp encoding per the encoder steps in the specification.">
@@ -15,51 +30,15 @@
 <script src="jis0208_index.js"></script>
 <script src="jis0212_index.js"></script>
 <script src="eucjp-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(eucjpDecoder);">
 
 <iframe src="eucjp_chars.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-    return frameRef.contentWindow
-        ? frameRef.contentWindow.document
-        : frameRef.contentDocument;
-}
-
-function showNodes() {
-    var iframe = iframeRef(document.getElementById("scrwin"));
-    nodes = iframe.querySelectorAll("span");
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i] = async_test(
-            "U+" +
-                nodes[i].dataset.cp +
-                " " +
-                String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-                " " +
-                eucjpDecoder(nodes[i].dataset.bytes) +
-                " " +
-                nodes[i].dataset.bytes
-        );
-    }
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i].step(function() {
-            assert_equals(
-                nodes[i].textContent,
-                eucjpDecoder(nodes[i].dataset.bytes)
-            );
-        });
-        tests[i].done();
-    }
-}
-</script>
 </body>
 </html>
 
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-cseucpkdfmtjapanese.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-cseucpkdfmtjapanese.html
index f64f0ed..83a674b 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-cseucpkdfmtjapanese.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-cseucpkdfmtjapanese.html
@@ -4,8 +4,17 @@
 <meta charset="cseucpkdfmtjapanese"><!-- test breaks if the server overrides this -->
 <title>cseucpkdfmtjapanese encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "cseucpkdfmtjapanese";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = eucjpEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-han.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-han.html
index f79178a..0d7585c 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-han.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-han.html
@@ -4,8 +4,31 @@
 <meta charset="euc-jp"> <!-- test breaks if the server overrides this -->
 <title>EUC-JP encoding errors (form, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-21000">
+<meta name="variant" content="?21001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,167 +41,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (i = 0x4e00; i < 0x9fba; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "cjk ";
-        }
-    }
-
-    for (i = 0xf900; i < 0xfa6e; i++) {
-        // compatibility
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "compatibility ";
-        }
-    }
-
-    for (i = 0xfa70; i < 0xfada; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "compatibility ";
-        }
-    }
-
-    for (i = 0x3400; i < 0x4dbf; i++) {
-        // cjk extension A
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "extension A ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    " U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "euc-jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = eucjpEncoder;
+var ranges = rangesHan;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-hangul.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-hangul.html
index 0f9ecff..8c70e8f 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-hangul.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-hangul.html
@@ -4,8 +4,21 @@
 <meta charset="euc-jp"> <!-- test breaks if the server overrides this -->
 <title>EUC-JP encoding errors (form, hangul)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,132 +31,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (i = 0xac00; i < 0xd7af; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "hangul ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "euc-jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = eucjpEncoder;
+var ranges = rangesHangul;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-misc.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-misc.html
index 54da3eb..c80193c 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-misc.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-errors-misc.html
@@ -4,8 +4,13 @@
 <meta charset="euc-jp"> <!-- test breaks if the server overrides this -->
 <title>EUC-JP encoding errors (form, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,176 +23,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (var i = 0x80; i < 0x4ff; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "latin, greek, cyrillic, etc ";
-        }
-    }
-
-    for (i = 0x2000; i < 0x23ff; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "punctuation, currency, symbols ";
-        }
-    }
-
-    for (i = 0x2460; i < 0x26ff; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "enclosed chars and boxes ";
-        }
-    }
-
-    for (i = 0x3000; i < 0x33ff; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "various asian ";
-        }
-    }
-
-    for (i = 0xff00; i < 0xffef; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "half/full width ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    " U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "euc-jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = eucjpEncoder;
+var ranges = rangesMisc;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-x-euc-jp.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-x-euc-jp.html
index 37ec8f8..30c376c 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-x-euc-jp.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form-x-euc-jp.html
@@ -4,8 +4,17 @@
 <meta charset="x-euc-jp"><!-- test breaks if the server overrides this -->
 <title>x-euc-jp encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "x-euc-jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = eucjpEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form.html
index 297ed84..cd6bcdc 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-form.html
@@ -4,8 +4,17 @@
 <meta charset="euc-jp"><!-- test breaks if the server overrides this -->
 <title>EUC-JP encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = eucjpEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "euc-jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = eucjpEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-han.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-han.html
index c0519e8..5883c00 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-han.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-han.html
@@ -4,97 +4,56 @@
 <meta charset="euc-jp"> <!-- test breaks if the server overrides this -->
 <title>EUC-JP encoding errors (href, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-10000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-21000">
+<meta name="variant" content="?21001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-jp">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding han characters that are not in the euc-jp encoding.">
-<script>
-function encode(input, expected, desc) {
-    // tests whether a Unicode character is converted to an equivalent byte sequence by href
-    // input: a Unicode character
-    // expected: expected byte sequence
-    // desc: what's being tested
-    test(function() {
-        var a = document.createElement("a"); // <a> uses document encoding for URL's query
-        a.href = "https://example.com/?" + input;
-        result = a.search.substr(1); // remove leading "?"
-        assert_equals(result, expected);
-    }, desc);
-}
-
-// set up a simple array of unicode codepoints that are not encoded
-var codepoints = [];
-
-for (i = 0x4e00; i < 0x9fba; i++) {
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "cjk ";
-    }
-}
-
-for (i = 0xf900; i < 0xfa6e; i++) {
-    // compatibility
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "compatibility ";
-    }
-}
-
-for (i = 0xfa70; i < 0xfada; i++) {
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "compatibility ";
-    }
-}
-
-for (i = 0x3400; i < 0x4dbf; i++) {
-    // cjk extension A
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "extension A ";
-    }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-    encode(
-        String.fromCodePoint(codepoints[x].cp),
-        codepoints[x].expected,
-        codepoints[x].desc +
-            " U+" +
-            codepoints[x].cp.toString(16).toUpperCase() +
-            " " +
-            String.fromCodePoint(codepoints[x].cp) +
-            " " +
-            codepoints[x].expected
-    );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = eucjpEncoder;
+var ranges = rangesHan;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
+}
+</script>
+<script src="../../resources/encode-href-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-hangul.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-hangul.html
index 29c9f22..695aad8 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-hangul.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-hangul.html
@@ -4,62 +4,46 @@
 <meta charset="euc-jp"> <!-- test breaks if the server overrides this -->
 <title>EUC-JP encoding errors (href, hangul)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-10000">
+<meta name="variant" content="?11001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-jp">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding hangul characters that are not in the euc-jp encoding.">
-<script>
-function encode(input, expected, desc) {
-    // tests whether a Unicode character is converted to an equivalent byte sequence by href
-    // input: a Unicode character
-    // expected: expected byte sequence
-    // desc: what's being tested
-    test(function() {
-        var a = document.createElement("a"); // <a> uses document encoding for URL's query
-        a.href = "https://example.com/?" + input;
-        result = a.search.substr(1); // remove leading "?"
-        assert_equals(result, expected);
-    }, desc);
-}
-
-// set up a simple array of  unicode codepoints that are not encoded
-var codepoints = [];
-
-for (i = 0xac00; i < 0xd7af; i++) {
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "hangul ";
-    }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-    encode(
-        String.fromCodePoint(codepoints[x].cp),
-        codepoints[x].expected,
-        codepoints[x].desc +
-            " U+" +
-            codepoints[x].cp.toString(16).toUpperCase() +
-            " " +
-            String.fromCodePoint(codepoints[x].cp) +
-            " " +
-            codepoints[x].expected
-    );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = eucjpEncoder;
+var ranges = rangesHangul;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
+}
+</script>
+<script src="../../resources/encode-href-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-misc.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-misc.html
index 1456b94..ba884f3 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-misc.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href-errors-misc.html
@@ -4,106 +4,38 @@
 <meta charset="euc-jp"> <!-- test breaks if the server overrides this -->
 <title>EUC-JP encoding errors (href, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-jp">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding miscellaneous characters that are not in the euc-jp encoding.">
-<script>
-function encode(input, expected, desc) {
-    // tests whether a Unicode character is converted to an equivalent byte sequence by href
-    // input: a Unicode character
-    // expected: expected byte sequence
-    // desc: what's being tested
-    test(function() {
-        var a = document.createElement("a"); // <a> uses document encoding for URL's query
-        a.href = "https://example.com/?" + input;
-        result = a.search.substr(1); // remove leading "?"
-        assert_equals(result, expected);
-    }, desc);
-}
-
-// set up a simple array of unicode codepoints that are not encoded
-var codepoints = [];
-
-for (var i = 0x80; i < 0x4ff; i++) {
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "latin, greek, cyrillic, etc ";
-    }
-}
-
-for (i = 0x2000; i < 0x23ff; i++) {
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "punctuation, currency, symbols ";
-    }
-}
-
-for (i = 0x2460; i < 0x26ff; i++) {
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "enclosed chars and boxes ";
-    }
-}
-
-for (i = 0x3000; i < 0x33ff; i++) {
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "various asian ";
-    }
-}
-
-for (i = 0xff00; i < 0xffef; i++) {
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "half/full width ";
-    }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-    encode(
-        String.fromCodePoint(codepoints[x].cp),
-        codepoints[x].expected,
-        codepoints[x].desc +
-            " U+" +
-            codepoints[x].cp.toString(16).toUpperCase() +
-            " " +
-            String.fromCodePoint(codepoints[x].cp) +
-            " " +
-            codepoints[x].expected
-    );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = eucjpEncoder;
+var ranges = rangesMisc;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
+}
+</script>
+<script src="../../resources/encode-href-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href.html b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href.html
index 05b6c2c..9d60eda 100644
--- a/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href.html
+++ b/encoding/legacy-mb-japanese/euc-jp/eucjp-encode-href.html
@@ -4,59 +4,38 @@
 <meta charset=euc-jp> <!-- test breaks if the server overrides this -->
 <title>EUC-JP encoding (href)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="eucjp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-jp">
 <meta name="assert" content="The browser produces the expected byte sequences for all characters in the euc-jp encoding after 0x9F when writing characters to an href value, using the encoder steps in the specification.">
-<script>
-function encode(input, expected, desc) {
-    // tests whether a Unicode character is converted to an equivalent byte sequence by href
-    // input: a Unicode character
-    // expected: expected byte sequence
-    // desc: what's being tested
-    test(function() {
-        var a = document.createElement("a"); // <a> uses document encoding for URL's query
-        a.href = "https://example.com/?" + input;
-        result = a.search.substr(1); // remove leading "?"
-        assert_equals(normalizeStr(result), normalizeStr(expected));
-    }, desc);
-}
-
-// create a simple list of just those code points for which there is an encoding possible
-codepoints = [];
-for (var i = 0x80; i < 0xffff; i++) {
-    result = eucjpEncoder(String.fromCodePoint(i));
-    if (result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%" + result.replace(/ /g, "%");
-    }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-    encode(
-        String.fromCodePoint(codepoints[x].cp),
-        codepoints[x].expected,
-        "U+" +
-            codepoints[x].cp.toString(16).toUpperCase() +
-            " " +
-            String.fromCodePoint(codepoints[x].cp) +
-            " " +
-            codepoints[x].expected
-    );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = false;
+var encoder = eucjpEncoder;
+var ranges = rangesAll;
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
+}
+</script>
+<script src="../../resources/encode-href-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-decode-csiso2022jp.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-decode-csiso2022jp.html
index 73d8a2f..d9dc2f0 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-decode-csiso2022jp.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-decode-csiso2022jp.html
@@ -4,8 +4,17 @@
 <meta charset="utf-8"/>
 <title>csiso2022jp decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#iso-2022-jp">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'csiso2022jp' as for a document labeled 'iso-2022-jp'.">
@@ -14,50 +23,14 @@
 </style>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(iso2022jpDecoder);">
 
 <iframe src="iso2022jp_chars-csiso2022jp.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-    return frameRef.contentWindow
-        ? frameRef.contentWindow.document
-        : frameRef.contentDocument;
-}
-
-function showNodes() {
-    var iframe = iframeRef(document.getElementById("scrwin"));
-    nodes = iframe.querySelectorAll("span");
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i] = async_test(
-            "U+" +
-                nodes[i].dataset.cp +
-                " " +
-                String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-                " " +
-                iso2022jpDecoder(nodes[i].dataset.bytes) +
-                " " +
-                nodes[i].dataset.bytes
-        );
-    }
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i].step(function() {
-            assert_equals(
-                nodes[i].textContent,
-                iso2022jpDecoder(nodes[i].dataset.bytes)
-            );
-        });
-        tests[i].done();
-    }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-decode.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-decode.html
index 192e25e..4434e54 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-decode.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-decode.html
@@ -4,8 +4,17 @@
 <meta charset="utf-8"/>
 <title>ISO 2022-JP decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#iso-2022-jp">
 <meta name="assert" content="The browser decodes all characters as expected from a file generated by encoding all pointers in the iso-2022-jp encoding per the encoder steps in the specification.">
@@ -14,50 +23,14 @@
 </style>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(iso2022jpDecoder);">
 
 <iframe src="iso2022jp_chars.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-    return frameRef.contentWindow
-        ? frameRef.contentWindow.document
-        : frameRef.contentDocument;
-}
-
-function showNodes() {
-    var iframe = iframeRef(document.getElementById("scrwin"));
-    nodes = iframe.querySelectorAll("span");
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i] = async_test(
-            "U+" +
-                nodes[i].dataset.cp +
-                " " +
-                String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-                " " +
-                iso2022jpDecoder(nodes[i].dataset.bytes) +
-                " " +
-                nodes[i].dataset.bytes
-        );
-    }
-
-    for (var i = 0; i < nodes.length; i++) {
-        tests[i].step(function() {
-            assert_equals(
-                nodes[i].textContent,
-                iso2022jpDecoder(nodes[i].dataset.bytes)
-            );
-        });
-        tests[i].done();
-    }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp.html
index 213791a..cd8d41b 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp.html
@@ -4,8 +4,17 @@
 <meta charset="csiso2022jp"> <!-- test breaks if the server overrides this -->
 <title>csiso2022jp encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,19 +27,19 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = "\u0019";
-var encodedSeperator = encodeURIComponent("\u0019");
-var currentChunkIndex = 0;
+var errors = false;
+var separator = "\u0019";
+var ranges = rangesAll;
+
+function expect(result, codepoint) {
+    return result;
+}
+
+function encoder(str) {
+    return getByteSequence(str.codePointAt(0));
+}
 
 function getByteSequence(cp) {
     // uses the Encoding spec algorithm to derive a sequence of bytes for a character that can be encoded
@@ -121,118 +130,7 @@
         indexcodepoints[jis0208[p]] = p;
     }
 }
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = getByteSequence(i);
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = result;
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    normalizeStr(item.expected)
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "csiso2022jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
-}
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-han.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-han.html
index 13d4b96..b288479 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-han.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-han.html
@@ -4,8 +4,30 @@
 <meta charset="iso-2022-jp"> <!-- test breaks if the server overrides this -->
 <title>ISO 2022 JP encoding errors (form, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,167 +40,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = "\u0019";
-var encodedSeperator = encodeURIComponent("\u0019");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (i = 0x4e00; i < 0x9fba; i++) {
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "cjk ";
-        }
-    }
-
-    for (i = 0xf900; i < 0xfa6e; i++) {
-        // compatibility
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "compatibility ";
-        }
-    }
-
-    for (i = 0xfa70; i < 0xfada; i++) {
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "compatibility ";
-        }
-    }
-
-    for (i = 0x3400; i < 0x4dbf; i++) {
-        // cjk extension A
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "extension A ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    " U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    normalizeStr(item.expected)
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "iso-2022-jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = iso2022jpEncoder;
+var ranges = rangesHan;
+var separator = "\u0019";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-hangul.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-hangul.html
index 1f1cce2..eb4d8d2 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-hangul.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-hangul.html
@@ -4,8 +4,21 @@
 <meta charset="iso-2022-jp"> <!-- test breaks if the server overrides this -->
 <title>ISO 2022 JP encoding errors (form, hangul)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,132 +31,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = "\u0019";
-var encodedSeperator = encodeURIComponent("\u0019");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (i = 0xac00; i < 0xd7af; i++) {
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "hangul ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    normalizeStr(item.expected)
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "iso-2022-jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = iso2022jpEncoder;
+var ranges = rangesHangul;
+var separator = "\u0019";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-misc.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-misc.html
index 54acbe4..95467ba 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-misc.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-misc.html
@@ -4,8 +4,13 @@
 <meta charset="iso-2022-jp"> <!-- test breaks if the server overrides this -->
 <title>ISO 2022 JP encoding errors (form, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,176 +23,24 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = "\u0019";
-var encodedSeperator = encodeURIComponent("\u0019");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (var i = 0xa0; i < 0x4ff; i++) {
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "latin, greek, cyrillic, etc ";
-        }
-    }
-
-    for (i = 0x2000; i < 0x23ff; i++) {
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "punctuation, currency, symbols ";
-        }
-    }
-
-    for (i = 0x2460; i < 0x26ff; i++) {
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "enclosed chars and boxes ";
-        }
-    }
-
-    for (i = 0x3000; i < 0x33ff; i++) {
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "various asian ";
-        }
-    }
-
-    for (i = 0xff00; i < 0xffef; i++) {
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "half/full width ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    " U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    normalizeStr(item.expected)
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "iso-2022-jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = iso2022jpEncoder;
+var ranges = rangesMisc;
+var separator = "\u0019";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form.html
index f196c31..3a1b3b9 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form.html
@@ -4,8 +4,17 @@
 <meta charset="iso-2022-jp"> <!-- test breaks if the server overrides this -->
 <title>ISO 2022 JP encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,129 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = "\u0019";
-var encodedSeperator = encodeURIComponent("\u0019");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = iso2022jpEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-            item.expected = item.expected.replace(/%1B%28%42$/, "");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "iso-2022-jp";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(normalizeStr(results[j]), cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = iso2022jpEncoder;
+var ranges = rangesAll;
+var separator = "\u0019";
+function expect(result, codepoint) {
+  return  "%" + result.replace(/ /g, "%").replace(/%1B%28%42$/, "");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-han.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-han.html
index f12da70..125d041 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-han.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-han.html
@@ -4,95 +4,49 @@
 <meta charset="iso-2022-jp"> <!-- test breaks if the server overrides this -->
 <title>ISO 2022-JP encoding errors (href, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#iso-2022-jp">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding han characters that are not in the iso-2022-jp encoding.">
+<script src="../../resources/ranges.js"></script>
 <script>
-function encode(input, expected, desc) {
-    // tests whether a Unicode character is converted to an equivalent byte sequence by href
-    // input: a Unicode character
-    // expected: expected byte sequence
-    // desc: what's being tested
-    test(function() {
-        var a = document.createElement("a"); // <a> uses document encoding for URL's query
-        a.href = "https://example.com/?" + input;
-        result = a.search.substr(1); // remove leading "?"
-        assert_equals(result, expected);
-    }, desc);
+var errors = true;
+var encoder = iso2022jpEncoder;
+var ranges = rangesHan;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
 }
-
-// set up a simple array of unicode codepoints that are not encoded
-var codepoints = [];
-
-for (i = 0x4e00; i < 0x9fba; i++) {
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "cjk ";
-    }
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
-
-for (i = 0xf900; i < 0xfa6e; i++) {
-    // compatibility
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "compatibility ";
-    }
-}
-
-for (i = 0xfa70; i < 0xfada; i++) {
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "compatibility ";
-    }
-}
-
-for (i = 0x3400; i < 0x4dbf; i++) {
-    // cjk extension A
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "extension A ";
-    }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-    encode(
-        String.fromCodePoint(codepoints[x].cp),
-        codepoints[x].expected,
-        codepoints[x].desc +
-            " U+" +
-            codepoints[x].cp.toString(16).toUpperCase() +
-            " " +
-            String.fromCodePoint(codepoints[x].cp) +
-            " " +
-            codepoints[x].expected
-    );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the test excludes ASCII characters
 </script>
+<script src="../../resources/encode-href-common.js"></script>
 </head>
 <body>
 <div id="log"></div>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-hangul.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-hangul.html
index 04dc69d..6e58835 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-hangul.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-hangul.html
@@ -4,60 +4,40 @@
 <meta charset="iso-2022-jp"> <!-- test breaks if the server overrides this -->
 <title>ISO 2022-JP encoding errors (href, hangul)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#iso-2022-jp">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding hangul characters that are not in the iso-2022-jp encoding.">
+<script src="../../resources/ranges.js"></script>
 <script>
-function encode(input, expected, desc) {
-    // tests whether a Unicode character is converted to an equivalent byte sequence by href
-    // input: a Unicode character
-    // expected: expected byte sequence
-    // desc: what's being tested
-    test(function() {
-        var a = document.createElement("a"); // <a> uses document encoding for URL's query
-        a.href = "https://example.com/?" + input;
-        result = a.search.substr(1); // remove leading "?"
-        assert_equals(result, expected);
-    }, desc);
+var errors = true;
+var encoder = iso2022jpEncoder;
+var ranges = rangesHangul;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
 }
-
-// set up a simple array of  unicode codepoints that are not encoded
-var codepoints = [];
-
-for (i = 0xac00; i < 0xd7af; i++) {
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "hangul ";
-    }
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-    encode(
-        String.fromCodePoint(codepoints[x].cp),
-        codepoints[x].expected,
-        codepoints[x].desc +
-            " U+" +
-            codepoints[x].cp.toString(16).toUpperCase() +
-            " " +
-            String.fromCodePoint(codepoints[x].cp) +
-            " " +
-            codepoints[x].expected
-    );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the test excludes ASCII characters
 </script>
+<script src="../../resources/encode-href-common.js"></script>
 </head>
 <body>
 <div id="log"></div>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-misc.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-misc.html
index 5fee8bb..8d8150d 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-misc.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-errors-misc.html
@@ -4,104 +4,32 @@
 <meta charset="iso-2022-jp"> <!-- test breaks if the server overrides this -->
 <title>ISO 2022-JP encoding errors (href, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#iso-2022-jp">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding miscellaneous characters that are not in the iso-2022-jp encoding.">
+<script src="../../resources/ranges.js"></script>
 <script>
-function encode(input, expected, desc) {
-    // tests whether a Unicode character is converted to an equivalent byte sequence by href
-    // input: a Unicode character
-    // expected: expected byte sequence
-    // desc: what's being tested
-    test(function() {
-        var a = document.createElement("a"); // <a> uses document encoding for URL's query
-        a.href = "https://example.com/?" + input;
-        result = a.search.substr(1); // remove leading "?"
-        assert_equals(result, expected);
-    }, desc);
+var errors = true;
+var encoder = iso2022jpEncoder;
+var ranges = rangesMisc;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
 }
-
-// set up a simple array of unicode codepoints that are not encoded
-var codepoints = [];
-
-for (var i = 0x80; i < 0x4ff; i++) {
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "latin, greek, cyrillic, etc ";
-    }
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
-
-for (i = 0x2000; i < 0x23ff; i++) {
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "punctuation, currency, symbols ";
-    }
-}
-
-for (i = 0x2460; i < 0x26ff; i++) {
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "enclosed chars and boxes ";
-    }
-}
-
-for (i = 0x3000; i < 0x33ff; i++) {
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "various asian ";
-    }
-}
-
-for (i = 0xff00; i < 0xffef; i++) {
-    result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (!result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%26%23" + item.cp + "%3B";
-        item.desc = "half/full width ";
-    }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-    encode(
-        String.fromCodePoint(codepoints[x].cp),
-        codepoints[x].expected,
-        codepoints[x].desc +
-            " U+" +
-            codepoints[x].cp.toString(16).toUpperCase() +
-            " " +
-            String.fromCodePoint(codepoints[x].cp) +
-            " " +
-            codepoints[x].expected
-    );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the test excludes ASCII characters
 </script>
+<script src="../../resources/encode-href-common.js"></script>
 </head>
 <body>
 <div id="log"></div>
diff --git a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href.html b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href.html
index dcea8fc..08b8c26 100644
--- a/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href.html
+++ b/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href.html
@@ -4,58 +4,32 @@
 <meta charset="iso-2022-jp"> <!-- test breaks if the server overrides this -->
 <title>ISO 2022-JP encoding (href)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="iso2022jp-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#iso-2022-jp">
 <meta name="assert" content="The browser produces the expected byte sequences for all characters in the iso-2022-jp encoding after 0x9F when writing characters to an href value, using the encoder steps in the specification.">
+<script src="../../resources/ranges.js"></script>
 <script>
-function encode(input, expected, desc) {
-    // tests whether a Unicode character is converted to an equivalent byte sequence by href
-    // input: a Unicode character
-    // expected: expected byte sequence
-    // desc: what's being tested
-    test(function() {
-        var a = document.createElement("a"); // <a> uses document encoding for URL's query
-        a.href = "https://example.com/?" + input;
-        result = a.search.substr(1); // remove leading "?"
-        assert_equals(normalizeStr(result), normalizeStr(expected));
-    }, desc);
+var errors = false;
+var encoder = iso2022jpEncoder;
+var ranges = rangesAll;
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%").replace(/%1B%28%42$/, "");;
 }
-
-// create a simple list of just those code points for which there is an encoding possible
-codepoints = [];
-for (var i = 0x80; i < 0xffff; i++) {
-    var result = iso2022jpEncoder(String.fromCodePoint(i));
-    if (result) {
-        var item = {};
-        codepoints.push(item);
-        item.cp = i;
-        item.expected = "%" + result.replace(/ /g, "%");
-        item.expected = item.expected.replace(/%1B%28%42$/, "");
-    }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-    encode(
-        String.fromCodePoint(codepoints[x].cp),
-        codepoints[x].expected,
-        "U+" +
-            codepoints[x].cp.toString(16).toUpperCase() +
-            " " +
-            String.fromCodePoint(codepoints[x].cp) +
-            " " +
-            codepoints[x].expected
-    );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the test excludes ASCII characters
 </script>
+<script src="../../resources/encode-href-common.js"></script>
 </head>
 <body>
 <div id="log"></div>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-csshiftjis.html b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-csshiftjis.html
index 9c811a9..d689375 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-csshiftjis.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-csshiftjis.html
@@ -4,8 +4,17 @@
 <meta charset="utf-8"/>
 <title>csshiftjis decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'csshiftjis' as for a document labeled 'shift_jis'.">
@@ -14,47 +23,15 @@
 </style>
 <script src="jis0208_index.js"></script>
 <script src="sjis-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(sjisDecoder);">
 
 <iframe src="sjis_chars-csshiftjis.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
 
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        sjisDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, sjisDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-errors.html b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-errors.html
index be96f70..50ac2b7 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-errors.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-errors.html
@@ -7,7 +7,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
 <meta name="assert" content="The browser decodes characters that are not recognised from the shift_jis index as replacement characters.">
 <style>
  iframe { display:none }
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms932.html b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms932.html
index a73294f..e65a486 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms932.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms932.html
@@ -4,8 +4,17 @@
 <meta charset="utf-8"/>
 <title>ms932 decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'ms932' as for a document labeled 'shift_jis'.">
@@ -14,47 +23,14 @@
 </style>
 <script src="jis0208_index.js"></script>
 <script src="sjis-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(sjisDecoder);">
 
 <iframe src="sjis_chars-ms932.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        sjisDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, sjisDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms_kanji.html b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms_kanji.html
index 2b2c298..c59b1da 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms_kanji.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms_kanji.html
@@ -4,8 +4,17 @@
 <meta charset="utf-8"/>
 <title>ms_kanji decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'ms_kanji' as for a document labeled 'shift_jis'.">
@@ -14,47 +23,14 @@
 </style>
 <script src="jis0208_index.js"></script>
 <script src="sjis-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(sjisDecoder);">
 
 <iframe src="sjis_chars-ms_kanji.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        sjisDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, sjisDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-shift-jis.html b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-shift-jis.html
index dabb661..62159d1 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-shift-jis.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-shift-jis.html
@@ -4,8 +4,17 @@
 <meta charset="utf-8"/>
 <title>shift-jis decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'shift-jis' as for a document labeled 'shift_jis'.">
@@ -14,47 +23,14 @@
 </style>
 <script src="jis0208_index.js"></script>
 <script src="sjis-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(sjisDecoder);">
 
 <iframe src="sjis_chars-shift-jis.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        sjisDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, sjisDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-sjis.html b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-sjis.html
index d66b518..9c4047e 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-sjis.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-sjis.html
@@ -4,8 +4,17 @@
 <meta charset="utf-8"/>
 <title>sjis decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'sjis' as for a document labeled 'shift_jis'.">
@@ -14,47 +23,14 @@
 </style>
 <script src="jis0208_index.js"></script>
 <script src="sjis-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(sjisDecoder);">
 
 <iframe src="sjis_chars-sjis.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        sjisDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, sjisDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-windows-31j.html b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-windows-31j.html
index 099cba6..2ed617b 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-windows-31j.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-windows-31j.html
@@ -4,8 +4,17 @@
 <meta charset="utf-8"/>
 <title>windows-31j decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'windows-31j' as for a document labeled 'shift_jis'.">
@@ -14,47 +23,14 @@
 </style>
 <script src="jis0208_index.js"></script>
 <script src="sjis-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(sjisDecoder);">
 
 <iframe src="sjis_chars-windows-31j.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        sjisDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, sjisDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-x-sjis.html b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-x-sjis.html
index 5ffa461..02e6a5d 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-decode-x-sjis.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-decode-x-sjis.html
@@ -4,8 +4,17 @@
 <meta charset="utf-8"/>
 <title>x-sjis decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'x-sjis' as for a document labeled 'shift_jis'.">
@@ -14,47 +23,14 @@
 </style>
 <script src="jis0208_index.js"></script>
 <script src="sjis-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(sjisDecoder);">
 
 <iframe src="sjis_chars-x-sjis.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        sjisDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, sjisDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-decode.html b/encoding/legacy-mb-japanese/shift_jis/sjis-decode.html
index e0355c2..b05bc28 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-decode.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-decode.html
@@ -4,57 +4,33 @@
 <meta charset="utf-8"/>
 <title>ShiftJIS decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
 <meta name="assert" content="The browser decodes all characters as expected from a file generated by encoding all pointers in the shift_jis encoding per the shift_jis encoder steps in the specification.">
 <style>
  iframe { display:none }
 </style>
 <script src="jis0208_index.js"></script>
 <script src="sjis-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(sjisDecoder);">
 
 <iframe src="sjis_chars.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        sjisDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, sjisDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-csshiftjis.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-csshiftjis.html
index dd21b76..90002ca 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-csshiftjis.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-csshiftjis.html
@@ -4,13 +4,22 @@
 <meta charset="csshiftjis"> <!-- test breaks if the server overrides this -->
 <title>csshiftjis encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
-<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'csshiftjis' as for a document labeled 'euc-jp'.">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
+<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'csshiftjis' as for a document labeled 'shift_jis'.">
 <style>
  iframe { display:none }
  form { display:none }
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
+var errors = false;
+var encoder = sjisEncoder;
+var ranges = rangesAll;
 var separator = ",";
-var encodedSeparator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = sjisEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "csshiftjis";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(separator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeparator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-han.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-han.html
index 8f80243..0c3f661 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-han.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-han.html
@@ -4,12 +4,34 @@
 <meta charset="shift_jis"> <!-- test breaks if the server overrides this -->
 <title>Shift_jis encoding errors (form, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
 <meta name="assert" content="The browser produces percent-escaped character references for a URL produced by a form when encoding han characters that are not in the shift_jis encoding.">
 <style>
  iframe { display:none }
@@ -18,162 +40,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-  // set up a simple array of unicode codepoints that are not encoded
-  var codepoints = [];
-
-  for (i = 0x4e00; i < 0x9fba; i++) {
-    result = sjisEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "cjk ";
-    }
-  }
-
-  for (i = 0xf900; i < 0xfa6e; i++) {
-    // compatibility
-    result = sjisEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "compatibility ";
-    }
-  }
-
-  for (i = 0xfa70; i < 0xfada; i++) {
-    result = sjisEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "compatibility ";
-    }
-  }
-
-  for (i = 0x3400; i < 0x4dbf; i++) {
-    // cjk extension A
-    result = sjisEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "extension A ";
-    }
-  }
-
-  // convert the information into a simple array of objects that can be easily traversed
-  var currentChunk = [];
-  var currentTests = [];
-  cplist = [currentChunk];
-  tests = [currentTests];
-  for (i = 0; i < codepoints.length; i++) {
-    if (currentChunk.length == chunkSize) {
-      currentChunk = [];
-      cplist.push(currentChunk);
-      currentTests = [];
-      tests.push(currentTests);
-    }
-    var item = {};
-    currentChunk.push(item);
-    item.cp = codepoints[i].cp;
-    item.expected = codepoints[i].expected;
-    item.desc = codepoints[i].desc;
-    currentTests.push(
-      async_test(
-        item.desc +
-          " U+" +
-          item.cp.toString(16).toUpperCase() +
-          " " +
-          String.fromCodePoint(item.cp) +
-          item.expected
-      )
-    );
-  }
-
-  numChunks = cplist.length;
-
-  for (var i = 0; i < numFrames; i++) {
-    var frame = document.createElement("iframe");
-    frame.id = frame.name = "frame-" + i;
-    document.body.appendChild(frame);
-    var form = document.createElement("form");
-    form.id = "form-" + i;
-    form.method = "GET";
-    form.action = "/common/blank.html";
-    form.acceptCharset = "shift_jis";
-    form.target = frame.id;
-    var input = document.createElement("input");
-    input.id = input.name = "input-" + i;
-    form.appendChild(input);
-    document.body.appendChild(form);
-  }
-
-  addEventListener("load", function() {
-    frames = Array.prototype.slice.call(
-      document.getElementsByTagName("iframe")
-    );
-    forms = Array.prototype.slice.call(document.getElementsByTagName("form"));
-    inputs = Array.prototype.slice.call(document.getElementsByTagName("input"));
-    for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-      runNext(i);
-    }
-  });
-});
-
-function runNext(id) {
-  var i = currentChunkIndex;
-  currentChunkIndex += 1;
-
-  var iframe = frames[id];
-  var form = forms[id];
-  var input = inputs[id];
-
-  input.value = cplist[i]
-    .map(function(x) {
-      return String.fromCodePoint(x.cp);
-    })
-    .join(seperator);
-  form.submit();
-
-  iframe.onload = function() {
-    var url = iframe.contentWindow.location;
-    var query = url.search;
-    var result_string = query.substr(query.indexOf("=") + 1);
-    var results = result_string.split(encodedSeperator);
-
-    for (var j = 0; j < cplist[i].length; j++) {
-      var t = tests[i][j];
-      t.step(function() {
-        assert_equals(results[j], cplist[i][j].expected);
-      });
-      t.done();
-    }
-    if (currentChunkIndex < numChunks) {
-      runNext(id);
-    }
-  };
+var errors = true;
+var encoder = sjisEncoder;
+var ranges = rangesHan;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-hangul.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-hangul.html
index b2a953e..b62829f 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-hangul.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-hangul.html
@@ -4,12 +4,25 @@
 <meta charset="shift_jis"> <!-- test breaks if the server overrides this -->
 <title>Shift_jis encoding errors (form, hangul)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
 <meta name="assert" content="The browser produces percent-escaped character references for a URL produced by a form when encoding hangul characters that are not in the shift_jis encoding.">
 <style>
  iframe { display:none }
@@ -18,132 +31,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (i = 0xac00; i < 0xd7af; i++) {
-        result = sjisEncoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "hangul ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "shift_jis";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = sjisEncoder;
+var ranges = rangesHangul;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-misc.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-misc.html
index 0dc90d1..5bb89fe 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-misc.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-misc.html
@@ -4,12 +4,17 @@
 <meta charset="shift_jis"> <!-- test breaks if the server overrides this -->
 <title>Shift_jis encoding errors (form, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
 <meta name="assert" content="The browser produces percent-escaped character references for a URL produced by a form when encoding miscellaneous characters that are not in the shift_jis encoding.">
 <style>
  iframe { display:none }
@@ -18,172 +23,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-  // set up a simple array of unicode codepoints that are not encoded
-  var codepoints = [];
-
-  for (var i = 0x80; i < 0x4ff; i++) {
-    result = sjisEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "latin, greek, cyrillic, etc ";
-    }
-  }
-
-  for (i = 0x2000; i < 0x23ff; i++) {
-    result = sjisEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "punctuation, currency, symbols ";
-    }
-  }
-
-  for (i = 0x2460; i < 0x26ff; i++) {
-    result = sjisEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "enclosed chars and boxes ";
-    }
-  }
-
-  for (i = 0x3000; i < 0x33ff; i++) {
-    result = sjisEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "various asian ";
-    }
-  }
-
-  for (i = 0xff00; i < 0xffef; i++) {
-    result = sjisEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "half/full width ";
-    }
-  }
-
-  // convert the information into a simple array of objects that can be easily traversed
-  var currentChunk = [];
-  var currentTests = [];
-  cplist = [currentChunk];
-  tests = [currentTests];
-  for (i = 0; i < codepoints.length; i++) {
-    if (currentChunk.length == chunkSize) {
-      currentChunk = [];
-      cplist.push(currentChunk);
-      currentTests = [];
-      tests.push(currentTests);
-    }
-    var item = {};
-    currentChunk.push(item);
-    item.cp = codepoints[i].cp;
-    item.expected = codepoints[i].expected;
-    item.desc = codepoints[i].desc;
-    currentTests.push(
-      async_test(
-        item.desc +
-          " U+" +
-          item.cp.toString(16).toUpperCase() +
-          " " +
-          String.fromCodePoint(item.cp) +
-          " " +
-          item.expected
-      )
-    );
-  }
-
-  numChunks = cplist.length;
-
-  for (var i = 0; i < numFrames; i++) {
-    var frame = document.createElement("iframe");
-    frame.id = frame.name = "frame-" + i;
-    document.body.appendChild(frame);
-    var form = document.createElement("form");
-    form.id = "form-" + i;
-    form.method = "GET";
-    form.action = "/common/blank.html";
-    form.acceptCharset = "shift_jis";
-    form.target = frame.id;
-    var input = document.createElement("input");
-    input.id = input.name = "input-" + i;
-    form.appendChild(input);
-    document.body.appendChild(form);
-  }
-
-  addEventListener("load", function() {
-    frames = Array.prototype.slice.call(
-      document.getElementsByTagName("iframe")
-    );
-    forms = Array.prototype.slice.call(document.getElementsByTagName("form"));
-    inputs = Array.prototype.slice.call(document.getElementsByTagName("input"));
-    for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-      runNext(i);
-    }
-  });
-});
-
-function runNext(id) {
-  var i = currentChunkIndex;
-  currentChunkIndex += 1;
-
-  var iframe = frames[id];
-  var form = forms[id];
-  var input = inputs[id];
-
-  input.value = cplist[i]
-    .map(function(x) {
-      return String.fromCodePoint(x.cp);
-    })
-    .join(seperator);
-  form.submit();
-
-  iframe.onload = function() {
-    var url = iframe.contentWindow.location;
-    var query = url.search;
-    var result_string = query.substr(query.indexOf("=") + 1);
-    var results = result_string.split(encodedSeperator);
-
-    for (var j = 0; j < cplist[i].length; j++) {
-      var t = tests[i][j];
-      t.step(function() {
-        assert_equals(results[j], cplist[i][j].expected);
-      });
-      t.done();
-    }
-    if (currentChunkIndex < numChunks) {
-      runNext(id);
-    }
-  };
+var errors = true;
+var encoder = sjisEncoder;
+var ranges = rangesMisc;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms932.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms932.html
index f18bf66..bc440f3 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms932.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms932.html
@@ -4,13 +4,22 @@
 <meta charset="ms932"> <!-- test breaks if the server overrides this -->
 <title>ms932 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
-<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'ms932' as for a document labeled 'euc-jp'.">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
+<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'ms932' as for a document labeled 'shift_jis'.">
 <style>
  iframe { display:none }
  form { display:none }
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
+var errors = false;
+var encoder = sjisEncoder;
+var ranges = rangesAll;
 var separator = ",";
-var encodedSeparator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = sjisEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "ms932";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(separator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeparator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms_kanji.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms_kanji.html
index 5b09074..8de1fbf 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms_kanji.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms_kanji.html
@@ -4,13 +4,22 @@
 <meta charset="ms_kanji"> <!-- test breaks if the server overrides this -->
 <title>ms_kanji encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
-<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'ms_kanji' as for a document labeled 'euc-jp'.">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
+<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'ms_kanji' as for a document labeled 'shift_jis'.">
 <style>
  iframe { display:none }
  form { display:none }
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
+var errors = false;
+var encoder = sjisEncoder;
+var ranges = rangesAll;
 var separator = ",";
-var encodedSeparator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = sjisEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "ms_kanji";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(separator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeparator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-shift-jis.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-shift-jis.html
index cd6bb6e..7a3bd86 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-shift-jis.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-shift-jis.html
@@ -4,13 +4,22 @@
 <meta charset="shift-jis"> <!-- test breaks if the server overrides this -->
 <title>shift-jis encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
-<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'shift-jis' as for a document labeled 'euc-jp'.">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
+<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'shift-jis' as for a document labeled 'shift_jis'.">
 <style>
  iframe { display:none }
  form { display:none }
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
+var errors = false;
+var encoder = sjisEncoder;
+var ranges = rangesAll;
 var separator = ",";
-var encodedSeparator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = sjisEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "shift-jis";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(separator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeparator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-sjis.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-sjis.html
index ddade82..c6ff9b0 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-sjis.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-sjis.html
@@ -4,13 +4,22 @@
 <meta charset="sjis"> <!-- test breaks if the server overrides this -->
 <title>sjis encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
-<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'sjis' as for a document labeled 'euc-jp'.">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
+<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'sjis' as for a document labeled 'shift_jis'.">
 <style>
  iframe { display:none }
  form { display:none }
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
+var errors = false;
+var encoder = sjisEncoder;
+var ranges = rangesAll;
 var separator = ",";
-var encodedSeparator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = sjisEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "sjis";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(separator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeparator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-windows-31j.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-windows-31j.html
index 335f622..596f011 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-windows-31j.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-windows-31j.html
@@ -4,13 +4,22 @@
 <meta charset="windows-31j"> <!-- test breaks if the server overrides this -->
 <title>windows-31j encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
-<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'windows-31j' as for a document labeled 'euc-jp'.">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
+<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'windows-31j' as for a document labeled 'shift_jis'.">
 <style>
  iframe { display:none }
  form { display:none }
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
+var errors = false;
+var encoder = sjisEncoder;
+var ranges = rangesAll;
 var separator = ",";
-var encodedSeparator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = sjisEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "windows-31j";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(separator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeparator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-x-sjis.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-x-sjis.html
index 1d754ff..674c83d 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-x-sjis.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-x-sjis.html
@@ -4,13 +4,22 @@
 <meta charset="x-sjis"> <!-- test breaks if the server overrides this -->
 <title>x-sjis encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
-<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'x-sjis' as for a document labeled 'euc-jp'.">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
+<meta name="assert" content="The browser produces the same encoding behavior for a document labeled 'x-sjis' as for a document labeled 'shift_jis'.">
 <style>
  iframe { display:none }
  form { display:none }
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
+var errors = false;
+var encoder = sjisEncoder;
+var ranges = rangesAll;
 var separator = ",";
-var encodedSeparator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = sjisEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "x-sjis";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(separator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeparator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form.html
index 66d1483..5aa96ac 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form.html
@@ -4,13 +4,22 @@
 <meta charset="shift_jis"> <!-- test breaks if the server overrides this -->
 <title>Shift_jis encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
-<meta name="assert" content="The browser produces the expected byte sequences for all characters in the shift_jis encoding after 0x9F when encoding bytes for a URL produced by a form, using the  encoder steps in the specification.">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
+<meta name="assert" content="The browser produces the expected byte sequences for all characters in the shift_jis encoding after 0x9F when encoding bytes for a URL produced by a form, using the encoder steps in the specification.">
 <style>
  iframe { display:none }
  form { display:none }
@@ -18,131 +27,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
+var errors = false;
+var encoder = sjisEncoder;
+var ranges = rangesAll;
 var separator = ",";
-var encodedSeparator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = sjisEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "shift_jis";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(separator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeparator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-han.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-han.html
index 1f70550..0f883a6 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-han.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-han.html
@@ -4,97 +4,57 @@
 <meta charset="shift_jis"> <!-- test breaks if the server overrides this -->
 <title>Shift_jis encoding errors (href, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-10000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-21000">
+<meta name="variant" content="?21001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding han characters that are not in the shift_jis encoding.">
-<script>
-function encode(input, expected, desc) {
-	// tests whether a Unicode character is converted to an equivalent byte sequence by href
-	// input: a Unicode character
-	// expected: expected byte sequence
-	// desc: what's being tested
-	test(function() {
-		var a = document.createElement("a"); // <a> uses document encoding for URL's query
-		a.href = "https://example.com/?" + input;
-		result = a.search.substr(1); // remove leading "?"
-		assert_equals(result, expected);
-	}, desc);
-}
-
-// set up a simple array of unicode codepoints that are not encoded
-var codepoints = [];
-
-for (i = 0x4e00; i < 0x9fba; i++) {
-	result = sjisEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "cjk ";
-	}
-}
-
-for (i = 0xf900; i < 0xfa6e; i++) {
-	// compatibility
-	result = sjisEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "compatibility ";
-	}
-}
-
-for (i = 0xfa70; i < 0xfada; i++) {
-	result = sjisEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "compatibility ";
-	}
-}
-
-for (i = 0x3400; i < 0x4dbf; i++) {
-	// cjk extension A
-	result = sjisEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "extension A ";
-	}
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-	encode(
-		String.fromCodePoint(codepoints[x].cp),
-		codepoints[x].expected,
-		codepoints[x].desc +
-			" U+" +
-			codepoints[x].cp.toString(16).toUpperCase() +
-			" " +
-			String.fromCodePoint(codepoints[x].cp) +
-			" " +
-			codepoints[x].expected
-	);
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = sjisEncoder;
+var ranges = rangesHan;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
+}
+</script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-hangul.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-hangul.html
index 29ec1a5..9bc926b 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-hangul.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-hangul.html
@@ -4,62 +4,47 @@
 <meta charset="shift_jis"> <!-- test breaks if the server overrides this -->
 <title>Shift_jis encoding (href, hangul)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding hangul characters that are not in the shift_jis encoding.">
-<script>
-function encode(input, expected, desc) {
-  // tests whether a Unicode character is converted to an equivalent byte sequence by href
-  // input: a Unicode character
-  // expected: expected byte sequence
-  // desc: what's being tested
-  test(function() {
-    var a = document.createElement("a"); // <a> uses document encoding for URL's query
-    a.href = "https://example.com/?" + input;
-    result = a.search.substr(1); // remove leading "?"
-    assert_equals(result, expected);
-  }, desc);
-}
-
-// set up a simple array of  unicode codepoints that are not encoded
-var codepoints = [];
-
-for (i = 0xac00; i < 0xd7af; i++) {
-  result = sjisEncoder(String.fromCodePoint(i));
-  if (!result) {
-    var item = {};
-    codepoints.push(item);
-    item.cp = i;
-    item.expected = "%26%23" + item.cp + "%3B";
-    item.desc = "hangul ";
-  }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-  encode(
-    String.fromCodePoint(codepoints[x].cp),
-    codepoints[x].expected,
-    codepoints[x].desc +
-      " U+" +
-      codepoints[x].cp.toString(16).toUpperCase() +
-      " " +
-      String.fromCodePoint(codepoints[x].cp) +
-      " " +
-      codepoints[x].expected
-  );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = sjisEncoder;
+var ranges = rangesHangul;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
+}
+</script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-misc.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-misc.html
index 8341d83..890c397 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-misc.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-misc.html
@@ -4,106 +4,39 @@
 <meta charset="shift_jis"> <!-- test breaks if the server overrides this -->
 <title>Shift_jis encoding (href, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding miscellaneous characters that are not in the shift_jis encoding.">
-<script>
-function encode(input, expected, desc) {
-	// tests whether a Unicode character is converted to an equivalent byte sequence by href
-	// input: a Unicode character
-	// expected: expected byte sequence
-	// desc: what's being tested
-	test(function() {
-		var a = document.createElement("a"); // <a> uses document encoding for URL's query
-		a.href = "https://example.com/?" + input;
-		result = a.search.substr(1); // remove leading "?"
-		assert_equals(result, expected);
-	}, desc);
-}
-
-// set up a simple array of unicode codepoints that are not encoded
-var codepoints = [];
-
-for (var i = 0x80; i < 0x4ff; i++) {
-	result = sjisEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "latin, greek, cyrillic, etc ";
-	}
-}
-
-for (i = 0x2000; i < 0x23ff; i++) {
-	result = sjisEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "punctuation, currency, symbols ";
-	}
-}
-
-for (i = 0x2460; i < 0x26ff; i++) {
-	result = sjisEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "enclosed chars and boxes ";
-	}
-}
-
-for (i = 0x3000; i < 0x33ff; i++) {
-	result = sjisEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "various asian ";
-	}
-}
-
-for (i = 0xff00; i < 0xffef; i++) {
-	result = sjisEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "half/full width ";
-	}
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-	encode(
-		String.fromCodePoint(codepoints[x].cp),
-		codepoints[x].expected,
-		codepoints[x].desc +
-			" U+" +
-			codepoints[x].cp.toString(16).toUpperCase() +
-			" " +
-			String.fromCodePoint(codepoints[x].cp) +
-			" " +
-			codepoints[x].expected
-	);
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = sjisEncoder;
+var ranges = rangesMisc;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
+}
+</script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href.html b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href.html
index 35c33fa..288e4da 100644
--- a/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href.html
+++ b/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href.html
@@ -4,59 +4,40 @@
 <meta charset="shift_jis"> <!-- test breaks if the server overrides this -->
 <title>Shift_jis encoding (href)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="jis0208_index.js"></script>
 <script src="sjis-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
-<link rel="help" href="https://encoding.spec.whatwg.org//#shift_jis">
+<link rel="help" href="https://encoding.spec.whatwg.org/#shift_jis">
 <meta name="assert" content="The browser produces the expected byte sequences for all characters in the shift_jis encoding after 0x9F when writing characters to an href value, using the encoder steps in the specification.">
-<script>
-function encode(input, expected, desc) {
-  // tests whether a Unicode character is converted to an equivalent byte sequence by href
-  // input: a Unicode character
-  // expected: expected byte sequence
-  // desc: what's being tested
-  test(function() {
-    var a = document.createElement("a"); // <a> uses document encoding for URL's query
-    a.href = "https://example.com/?" + input;
-    result = a.search.substr(1); // remove leading "?"
-    assert_equals(normalizeStr(result), normalizeStr(expected));
-  }, desc);
-}
-
-// create a simple list of just those code points for which there is an encoding possible
-codepoints = [];
-for (var i = 0x80; i < 0xffff; i++) {
-  result = sjisEncoder(String.fromCodePoint(i));
-  if (result) {
-    var item = {};
-    codepoints.push(item);
-    item.cp = i;
-    item.expected = "%" + result.replace(/ /g, "%");
-  }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-  encode(
-    String.fromCodePoint(codepoints[x].cp),
-    codepoints[x].expected,
-    "U+" +
-      codepoints[x].cp.toString(16).toUpperCase() +
-      " " +
-      String.fromCodePoint(codepoints[x].cp) +
-      " " +
-      codepoints[x].expected
-  );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = false;
+var encoder = sjisEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
+}
+</script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
+
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode-cseuckr.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode-cseuckr.html
index 4e1720d..8cd06a7 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode-cseuckr.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode-cseuckr.html
@@ -4,8 +4,27 @@
 <meta charset="utf-8"/>
 <title>cseuckr decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'cseuckr' as for a document labeled 'euc-kr'.">
@@ -14,47 +33,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars-cseuckr.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode-csksc56011987.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode-csksc56011987.html
index bedfd7d..6d7e6b0 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode-csksc56011987.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode-csksc56011987.html
@@ -4,8 +4,27 @@
 <meta charset="utf-8"/>
 <title>csksc56011987 decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'csksc56011987' as for a document labeled 'euc-kr'.">
@@ -14,47 +33,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars-csksc56011987.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode-iso-ir-149.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode-iso-ir-149.html
index ce71d3b..48e7fca 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode-iso-ir-149.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode-iso-ir-149.html
@@ -14,47 +14,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars-iso-ir-149.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode-korean.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode-korean.html
index 8a6971f..59ffdce 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode-korean.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode-korean.html
@@ -14,47 +14,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars-korean.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1987.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1987.html
index 5c38428..7a26f48 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1987.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1987.html
@@ -14,47 +14,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars-ks_c_5601-1987.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1989.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1989.html
index 09066b5..8c75cf5 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1989.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1989.html
@@ -14,47 +14,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars-ks_c_5601-1989.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc5601.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc5601.html
index 64e4e97..83c0922 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc5601.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc5601.html
@@ -14,47 +14,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars-ksc5601.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html
index 7429246..6499ab5 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html
@@ -14,47 +14,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars-ksc_5601.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode-windows-949.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode-windows-949.html
index 66e71af..1f2f302 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode-windows-949.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode-windows-949.html
@@ -14,47 +14,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars-windows-949.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-decode.html b/encoding/legacy-mb-korean/euc-kr/euckr-decode.html
index dab1302..a3b46f0 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-decode.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-decode.html
@@ -14,47 +14,14 @@
 </style>
 <script src="euckr_index.js"></script>
 <script src="euckr-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(euckrDecoder);">
 
 <iframe src="euckr_chars.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        euckrDecoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, euckrDecoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-cseuckr.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-cseuckr.html
index d8cdbe2..81143c3 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-cseuckr.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-cseuckr.html
@@ -4,8 +4,27 @@
 <meta charset="cseuckr"> <!-- test breaks if the server overrides this -->
 <title>cseuckr encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +37,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "cseuckr";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-csksc56011987.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-csksc56011987.html
index 8c6b9d6..2ee63e5 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-csksc56011987.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-csksc56011987.html
@@ -4,8 +4,27 @@
 <meta charset="csksc56011987"> <!-- test breaks if the server overrides this -->
 <title>csksc56011987 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +37,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "csksc56011987";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-han.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-han.html
index 033362d..8d79541 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-han.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-han.html
@@ -4,8 +4,33 @@
 <meta charset="euc-kr"> <!-- test breaks if the server overrides this -->
 <title>EUC-KR encoding errors (form, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-21000">
+<meta name="variant" content="?21001-22000">
+<meta name="variant" content="?22001-23000">
+<meta name="variant" content="?23001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,163 +43,19 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-  // set up a simple array of unicode codepoints that are not encoded
-  var codepoints = [];
-
-  for (i = 0x4e00; i < 0x9fba; i++) {
-    result = euckrEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "cjk ";
-    }
-  }
-
-  for (i = 0xf900; i < 0xfa6e; i++) {
-    // compatibility
-    result = euckrEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "compatibility ";
-    }
-  }
-
-  for (i = 0xfa70; i < 0xfada; i++) {
-    result = euckrEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "compatibility ";
-    }
-  }
-
-  for (i = 0x3400; i < 0x4dbf; i++) {
-    // cjk extension A
-    result = euckrEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "extension A ";
-    }
-  }
-
-  // convert the information into a simple array of objects that can be easily traversed
-  var currentChunk = [];
-  var currentTests = [];
-  cplist = [currentChunk];
-  tests = [currentTests];
-  for (i = 0; i < codepoints.length; i++) {
-    if (currentChunk.length == chunkSize) {
-      currentChunk = [];
-      cplist.push(currentChunk);
-      currentTests = [];
-      tests.push(currentTests);
-    }
-    var item = {};
-    currentChunk.push(item);
-    item.cp = codepoints[i].cp;
-    item.expected = codepoints[i].expected;
-    item.desc = codepoints[i].desc;
-    currentTests.push(
-      async_test(
-        item.desc +
-          " U+" +
-          item.cp.toString(16).toUpperCase() +
-          " " +
-          String.fromCodePoint(item.cp) +
-          " " +
-          item.expected
-      )
-    );
-  }
-
-  numChunks = cplist.length;
-
-  for (var i = 0; i < numFrames; i++) {
-    var frame = document.createElement("iframe");
-    frame.id = frame.name = "frame-" + i;
-    document.body.appendChild(frame);
-    var form = document.createElement("form");
-    form.id = "form-" + i;
-    form.method = "GET";
-    form.action = "/common/blank.html";
-    form.acceptCharset = "euc-kr";
-    form.target = frame.id;
-    var input = document.createElement("input");
-    input.id = input.name = "input-" + i;
-    form.appendChild(input);
-    document.body.appendChild(form);
-  }
-
-  addEventListener("load", function() {
-    frames = Array.prototype.slice.call(
-      document.getElementsByTagName("iframe")
-    );
-    forms = Array.prototype.slice.call(document.getElementsByTagName("form"));
-    inputs = Array.prototype.slice.call(document.getElementsByTagName("input"));
-    for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-      runNext(i);
-    }
-  });
-});
-
-function runNext(id) {
-  var i = currentChunkIndex;
-  currentChunkIndex += 1;
-
-  var iframe = frames[id];
-  var form = forms[id];
-  var input = inputs[id];
-
-  input.value = cplist[i]
-    .map(function(x) {
-      return String.fromCodePoint(x.cp);
-    })
-    .join(seperator);
-  form.submit();
-
-  iframe.onload = function() {
-    var url = iframe.contentWindow.location;
-    var query = url.search;
-    var result_string = query.substr(query.indexOf("=") + 1);
-    var results = result_string.split(encodedSeperator);
-
-    for (var j = 0; j < cplist[i].length; j++) {
-      var t = tests[i][j];
-      t.step(function() {
-        assert_equals(results[j], cplist[i][j].expected);
-      });
-      t.done();
-    }
-    if (currentChunkIndex < numChunks) {
-      runNext(id);
-    }
-  };
+var errors = true;
+var encoder = euckrEncoder;
+var ranges = rangesHan;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html.headers b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html.headers
deleted file mode 100644
index 3a990e8..0000000
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Content-Type: text/html; charset=euc-kr
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-misc.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-misc.html
index 130980f..4a7fa6d 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-misc.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-misc.html
@@ -4,8 +4,13 @@
 <meta charset="euc-kr"> <!-- test breaks if the server overrides this -->
 <title>EUC-KR encoding errors (form, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,172 +23,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-  // set up a simple array of unicode codepoints that are not encoded
-  var codepoints = [];
-
-  for (var i = 0x80; i < 0x4ff; i++) {
-    result = euckrEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "latin, greek, cyrillic, etc ";
-    }
-  }
-
-  for (i = 0x2000; i < 0x23ff; i++) {
-    result = euckrEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "punctuation, currency, symbols ";
-    }
-  }
-
-  for (i = 0x2460; i < 0x26ff; i++) {
-    result = euckrEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "enclosed chars and boxes ";
-    }
-  }
-
-  for (i = 0x3000; i < 0x33ff; i++) {
-    result = euckrEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "various asian ";
-    }
-  }
-
-  for (i = 0xff00; i < 0xffef; i++) {
-    result = euckrEncoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "half/full width ";
-    }
-  }
-
-  // convert the information into a simple array of objects that can be easily traversed
-  var currentChunk = [];
-  var currentTests = [];
-  cplist = [currentChunk];
-  tests = [currentTests];
-  for (i = 0; i < codepoints.length; i++) {
-    if (currentChunk.length == chunkSize) {
-      currentChunk = [];
-      cplist.push(currentChunk);
-      currentTests = [];
-      tests.push(currentTests);
-    }
-    var item = {};
-    currentChunk.push(item);
-    item.cp = codepoints[i].cp;
-    item.expected = codepoints[i].expected;
-    item.desc = codepoints[i].desc;
-    currentTests.push(
-      async_test(
-        item.desc +
-          " U+" +
-          item.cp.toString(16).toUpperCase() +
-          " " +
-          String.fromCodePoint(item.cp) +
-          " " +
-          item.expected
-      )
-    );
-  }
-
-  numChunks = cplist.length;
-
-  for (var i = 0; i < numFrames; i++) {
-    var frame = document.createElement("iframe");
-    frame.id = frame.name = "frame-" + i;
-    document.body.appendChild(frame);
-    var form = document.createElement("form");
-    form.id = "form-" + i;
-    form.method = "GET";
-    form.action = "/common/blank.html";
-    form.acceptCharset = "euc-kr";
-    form.target = frame.id;
-    var input = document.createElement("input");
-    input.id = input.name = "input-" + i;
-    form.appendChild(input);
-    document.body.appendChild(form);
-  }
-
-  addEventListener("load", function() {
-    frames = Array.prototype.slice.call(
-      document.getElementsByTagName("iframe")
-    );
-    forms = Array.prototype.slice.call(document.getElementsByTagName("form"));
-    inputs = Array.prototype.slice.call(document.getElementsByTagName("input"));
-    for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-      runNext(i);
-    }
-  });
-});
-
-function runNext(id) {
-  var i = currentChunkIndex;
-  currentChunkIndex += 1;
-
-  var iframe = frames[id];
-  var form = forms[id];
-  var input = inputs[id];
-
-  input.value = cplist[i]
-    .map(function(x) {
-      return String.fromCodePoint(x.cp);
-    })
-    .join(seperator);
-  form.submit();
-
-  iframe.onload = function() {
-    var url = iframe.contentWindow.location;
-    var query = url.search;
-    var result_string = query.substr(query.indexOf("=") + 1);
-    var results = result_string.split(encodedSeperator);
-
-    for (var j = 0; j < cplist[i].length; j++) {
-      var t = tests[i][j];
-      t.step(function() {
-        assert_equals(results[j], cplist[i][j].expected);
-      });
-      t.done();
-    }
-    if (currentChunkIndex < numChunks) {
-      runNext(id);
-    }
-  };
+var errors = true;
+var encoder = euckrEncoder;
+var ranges = rangesMisc;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-iso-ir-149.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-iso-ir-149.html
index 4ced58e..df309c0 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-iso-ir-149.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-iso-ir-149.html
@@ -4,8 +4,27 @@
 <meta charset="iso-ir-149"> <!-- test breaks if the server overrides this -->
 <title>iso-ir-149 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +37,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "iso-ir-149";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-korean.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-korean.html
index d7cae24..27b54f7 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-korean.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-korean.html
@@ -4,8 +4,27 @@
 <meta charset="korean"> <!-- test breaks if the server overrides this -->
 <title>korean encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,140 +37,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-// set up a sparse array of all unicode codepoints listed in the index
-// this will be used for lookup in getByteSequence
-var indexcodepoints = []; // index is unicode cp, value is pointer
-for (p = 0; p < euckr.length; p++) {
-    if (euckr[p] != null && indexcodepoints[euckr[p]] == null) {
-        indexcodepoints[euckr[p]] = p;
-    }
-}
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "korean";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1987.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1987.html
index f1f1b37..1160f88 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1987.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1987.html
@@ -4,8 +4,27 @@
 <meta charset="ks_c_5601-1987"> <!-- test breaks if the server overrides this -->
 <title>ks_c_5601-1987 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +37,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "ks_c_5601-1987";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1989.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1989.html
index 8a36d77..d8f1d80 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1989.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1989.html
@@ -4,8 +4,27 @@
 <meta charset="ks_c_5601-1989"> <!-- test breaks if the server overrides this -->
 <title>ks_c_5601-1989 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,140 +37,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-// set up a sparse array of all unicode codepoints listed in the index
-// this will be used for lookup in getByteSequence
-var indexcodepoints = []; // index is unicode cp, value is pointer
-for (p = 0; p < euckr.length; p++) {
-    if (euckr[p] != null && indexcodepoints[euckr[p]] == null) {
-        indexcodepoints[euckr[p]] = p;
-    }
-}
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "ks_c_5601-1989";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc5601.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc5601.html
index bc132bd..b6facc4 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc5601.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc5601.html
@@ -4,8 +4,27 @@
 <meta charset="ksc5601"> <!-- test breaks if the server overrides this -->
 <title>ksc5601 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +37,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "ksc5601";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc_5601.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc_5601.html
index f306e5b..78b9fe5 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc_5601.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc_5601.html
@@ -4,8 +4,27 @@
 <meta charset="ksc_5601"> <!-- test breaks if the server overrides this -->
 <title>ksc_5601 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +37,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "ksc_5601";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-windows-949.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-windows-949.html
index 3d2b4d1..aac3068 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-windows-949.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-windows-949.html
@@ -4,8 +4,28 @@
 <meta charset="windows-949"> <!-- test breaks if the server overrides this -->
 <title>windows-949 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
+
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +38,17 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "windows-949";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
+
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form.html
index 47e40aa..545f8ac 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-form.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-form.html
@@ -4,8 +4,27 @@
 <meta charset="euc-kr"> <!-- test breaks if the server overrides this -->
 <title>EUC-KR encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +37,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0xffff; i++) {
-        result = euckrEncoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "euc-kr";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-han.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-han.html
index ad334f9..91ef213 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-han.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-han.html
@@ -4,97 +4,58 @@
 <meta charset="euc-kr"> <!-- test breaks if the server overrides this -->
 <title>EUC-KR encoding errors (href, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-21000">
+<meta name="variant" content="?21001-22000">
+<meta name="variant" content="?22001-23000">
+<meta name="variant" content="?23001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-kr">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding han characters that are not in the euc-kr encoding.">
-<script>
-function encode(input, expected, desc) {
-	// tests whether a Unicode character is converted to an equivalent byte sequence by href
-	// input: a Unicode character
-	// expected: expected byte sequence
-	// desc: what's being tested
-	test(function() {
-		var a = document.createElement("a"); // <a> uses document encoding for URL's query
-		a.href = "https://example.com/?" + input;
-		result = a.search.substr(1); // remove leading "?"
-		assert_equals(result, expected);
-	}, desc);
-}
-
-// set up a simple array of unicode codepoints that are not encoded
-var codepoints = [];
-
-for (i = 0x4e00; i < 0x9fba; i++) {
-	result = euckrEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "cjk ";
-	}
-}
-
-for (i = 0xf900; i < 0xfa6e; i++) {
-	// compatibility
-	result = euckrEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "compatibility ";
-	}
-}
-
-for (i = 0xfa70; i < 0xfada; i++) {
-	result = euckrEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "compatibility ";
-	}
-}
-
-for (i = 0x3400; i < 0x4dbf; i++) {
-	// cjk extension A
-	result = euckrEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "extension A ";
-	}
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-	encode(
-		String.fromCodePoint(codepoints[x].cp),
-		codepoints[x].expected,
-		codepoints[x].desc +
-			" U+" +
-			codepoints[x].cp.toString(16).toUpperCase() +
-			" " +
-			String.fromCodePoint(codepoints[x].cp) +
-			" " +
-			codepoints[x].expected
-	);
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = euckrEncoder;
+var ranges = rangesHan;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
+}
+</script>
+<script src="../../resources/encode-href-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-misc.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-misc.html
index 5185963..a72a161 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-misc.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-misc.html
@@ -4,106 +4,38 @@
 <meta charset="euc-kr"> <!-- test breaks if the server overrides this -->
 <title>EUC-KR encoding errors (href, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-kr">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding miscellaneous characters that are not in the euc-kr encoding.">
-<script>
-function encode(input, expected, desc) {
-	// tests whether a Unicode character is converted to an equivalent byte sequence by href
-	// input: a Unicode character
-	// expected: expected byte sequence
-	// desc: what's being tested
-	test(function() {
-		var a = document.createElement("a"); // <a> uses document encoding for URL's query
-		a.href = "https://example.com/?" + input;
-		result = a.search.substr(1); // remove leading "?"
-		assert_equals(result, expected);
-	}, desc);
-}
-
-// set up a simple array of unicode codepoints that are not encoded
-var codepoints = [];
-
-for (var i = 0x80; i < 0x4ff; i++) {
-	result = euckrEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "latin, greek, cyrillic, etc ";
-	}
-}
-
-for (i = 0x2000; i < 0x23ff; i++) {
-	result = euckrEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "punctuation, currency, symbols ";
-	}
-}
-
-for (i = 0x2460; i < 0x26ff; i++) {
-	result = euckrEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "enclosed chars and boxes ";
-	}
-}
-
-for (i = 0x3000; i < 0x33ff; i++) {
-	result = euckrEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "various asian ";
-	}
-}
-
-for (i = 0xff00; i < 0xffef; i++) {
-	result = euckrEncoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "half/full width ";
-	}
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-	encode(
-		String.fromCodePoint(codepoints[x].cp),
-		codepoints[x].expected,
-		codepoints[x].desc +
-			" U+" +
-			codepoints[x].cp.toString(16).toUpperCase() +
-			" " +
-			String.fromCodePoint(codepoints[x].cp) +
-			" " +
-			codepoints[x].expected
-	);
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the tests exclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = euckrEncoder;
+var ranges = rangesMisc;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
+}
+</script>
+<script src="../../resources/encode-href-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-korean/euc-kr/euckr-encode-href.html b/encoding/legacy-mb-korean/euc-kr/euckr-encode-href.html
index f03a417..a4963fd 100644
--- a/encoding/legacy-mb-korean/euc-kr/euckr-encode-href.html
+++ b/encoding/legacy-mb-korean/euc-kr/euckr-encode-href.html
@@ -4,59 +4,48 @@
 <meta charset="euc-kr"> <!-- test breaks if the server overrides this -->
 <title>EUC-KR encoding (href)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="euckr_index.js"></script>
 <script src="euckr-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#euc-kr">
 <meta name="assert" content="The browser produces the expected byte sequences for all characters in the euc-kr encoding after 0x9F when writing characters to an href value, using the encoder steps in the specification.">
-<script>
-function encode(input, expected, desc) {
-  // tests whether a Unicode character is converted to an equivalent byte sequence by href
-  // input: a Unicode character
-  // expected: expected byte sequence
-  // desc: what's being tested
-  test(function() {
-    var a = document.createElement("a"); // <a> uses document encoding for URL's query
-    a.href = "https://example.com/?" + input;
-    result = a.search.substr(1); // remove leading "?"
-    assert_equals(normalizeStr(result), normalizeStr(expected));
-  }, desc);
-}
-
-// create a simple list of just those code points for which there is an encoding possible
-codepoints = [];
-for (var i = 0x80; i < 0xffff; i++) {
-  result = euckrEncoder(String.fromCodePoint(i));
-  if (result) {
-    var item = {};
-    codepoints.push(item);
-    item.cp = i;
-    item.expected = "%" + result.replace(/ /g, "%");
-  }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-  encode(
-    String.fromCodePoint(codepoints[x].cp),
-    codepoints[x].expected,
-    "U+" +
-      codepoints[x].cp.toString(16).toUpperCase() +
-      " " +
-      String.fromCodePoint(codepoints[x].cp) +
-      " " +
-      codepoints[x].expected
-  );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the testsexclude ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../ranges.js"></script>
+<script>
+var errors = false;
+var encoder = euckrEncoder;
+var ranges = rangesAll;
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
+}
+</script>
+<script src="../../encode-href-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-decode-big5-hkscs.html b/encoding/legacy-mb-tchinese/big5/big5-decode-big5-hkscs.html
index e3344f6..c9c137f 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-decode-big5-hkscs.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-decode-big5-hkscs.html
@@ -4,8 +4,24 @@
 <meta charset="utf-8"/>
 <title>big5-hkscs decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'big5-hkscs' as for a document labeled 'big5'.">
@@ -15,49 +31,14 @@
 </style>
 <script src="big5_index.js"></script>
 <script src="big5-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(big5Decoder);">
 
 <iframe src="big5_chars-big5-hkscs.html" name="scriptWindow" id="scrwin"></iframe>
 
-
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        big5Decoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, big5Decoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
-
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-decode-cn-big5.html b/encoding/legacy-mb-tchinese/big5/big5-decode-cn-big5.html
index 998b8a4..d553e6e 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-decode-cn-big5.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-decode-cn-big5.html
@@ -4,8 +4,24 @@
 <meta charset="utf-8"/>
 <title>cn-big5 decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'cn-big5' as for a document labeled 'big5'.">
@@ -15,49 +31,14 @@
 </style>
 <script src="big5_index.js"></script>
 <script src="big5-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(big5Decoder);">
 
 <iframe src="big5_chars-cn-big5.html" name="scriptWindow" id="scrwin"></iframe>
 
-
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        big5Decoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, big5Decoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
-
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-decode-csbig5.html b/encoding/legacy-mb-tchinese/big5/big5-decode-csbig5.html
index d107ecc..dc880d2 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-decode-csbig5.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-decode-csbig5.html
@@ -4,8 +4,24 @@
 <meta charset="utf-8"/>
 <title>csbig5 decoding</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels">
 <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'csbig5' as for a document labeled 'big5'.">
@@ -15,49 +31,14 @@
 </style>
 <script src="big5_index.js"></script>
 <script src="big5-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(big5Decoder);">
 
 <iframe src="big5_chars-csbig5.html" name="scriptWindow" id="scrwin"></iframe>
 
-
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        big5Decoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, big5Decoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
-
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-decode-extra.html b/encoding/legacy-mb-tchinese/big5/big5-decode-extra.html
index 7c5b0f9..9db3836 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-decode-extra.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-decode-extra.html
@@ -15,48 +15,14 @@
 </style>
 <script src="big5_index.js"></script>
 <script src="big5-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(big5Decoder);">
 
 <iframe src="big5_chars_extra.html" name="scriptWindow" id="scrwin"></iframe>
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        big5Decoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, big5Decoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
-
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-decode-x-x-big5.html b/encoding/legacy-mb-tchinese/big5/big5-decode-x-x-big5.html
index 31aec04..563a3c8 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-decode-x-x-big5.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-decode-x-x-big5.html
@@ -15,49 +15,14 @@
 </style>
 <script src="big5_index.js"></script>
 <script src="big5-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
-<body onload="showNodes();">
+<body onload="showNodes(big5Decoder);">
 
 <iframe src="big5_chars-x-x-big5.html" name="scriptWindow" id="scrwin"></iframe>
 
-
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        big5Decoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, big5Decoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
-
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-decode.html b/encoding/legacy-mb-tchinese/big5/big5-decode.html
index 5a00717..17b2a72 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-decode.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-decode.html
@@ -15,6 +15,7 @@
 </style>
 <script src="big5_index.js"></script>
 <script src="big5-decoder.js"></script>
+<script src="../../resources/decode-common.js"></script>
 </head>
 
 <body onload="showNodes();">
@@ -23,40 +24,5 @@
 
 <div id="log"></div>
 
-<script>
-var tests = [];
-
-function iframeRef(frameRef) {
-  return frameRef.contentWindow
-    ? frameRef.contentWindow.document
-    : frameRef.contentDocument;
-}
-
-function showNodes() {
-  var iframe = iframeRef(document.getElementById("scrwin"));
-  nodes = iframe.querySelectorAll("span");
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i] = async_test(
-      "U+" +
-        nodes[i].dataset.cp +
-        " " +
-        String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
-        " " +
-        big5Decoder(nodes[i].dataset.bytes) +
-        " " +
-        nodes[i].dataset.bytes
-    );
-  }
-
-  for (var i = 0; i < nodes.length; i++) {
-    tests[i].step(function() {
-      assert_equals(nodes[i].textContent, big5Decoder(nodes[i].dataset.bytes));
-    });
-    tests[i].done();
-  }
-}
-</script>
-
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-big5-hkscs.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-big5-hkscs.html
index 9fbfed6..ee5f0c9 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-big5-hkscs.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-big5-hkscs.html
@@ -4,8 +4,24 @@
 <meta charset="big5-hkscs"> <!-- test breaks if the server overrides this -->
 <title>big5-hkscs encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -17,133 +33,17 @@
 </style>
 </head>
 <body>
-
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0x2a6df; i++) {
-        result = big5Encoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "big5-hkscs";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = big5Encoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-cn-big5.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-cn-big5.html
index 04db127..4495f1b 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-cn-big5.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-cn-big5.html
@@ -4,8 +4,24 @@
 <meta charset="cn-big5"> <!-- test breaks if the server overrides this -->
 <title>cn-big5 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -17,133 +33,18 @@
 </style>
 </head>
 <body>
-
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0x2a6df; i++) {
-        result = big5Encoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "cn-big5";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = big5Encoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
+
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-csbig5.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-csbig5.html
index 410fa50..4d71c77 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-csbig5.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-csbig5.html
@@ -4,8 +4,24 @@
 <meta charset="csbig5"> <!-- test breaks if the server overrides this -->
 <title>csbig5 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -17,133 +33,17 @@
 </style>
 </head>
 <body>
-
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0x2a6df; i++) {
-        result = big5Encoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "csbig5";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = big5Encoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBa.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBa.html
index 752956f..d9eca7a 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBa.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBa.html
@@ -4,8 +4,31 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding errors (form, extB, part1)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-21000">
+<meta name="variant" content="?21001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,133 +41,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of miscellaneous unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (i = 0x20000; i < 0x2536b; i++) {
-        // cjk extension A
-        result = big5Encoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "extB (pt 1)";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "big5";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = big5Encoder;
+var ranges = rangesExtBa;
+var separator = ",";
+function expect(result, codepoint) {
+  return  "%26%23" + codepoint + "%3B";
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBb.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBb.html
index 186c7dc..b7406f2 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBb.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBb.html
@@ -4,8 +4,31 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding errors (form, extB, part2)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-21000">
+<meta name="variant" content="?21001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,133 +41,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of miscellaneous unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (i = 0x2536b; i < 0x2a6e0; i++) {
-        // cjk extension A
-        result = big5Encoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "extB (pt 2) ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "big5";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = big5Encoder;
+var ranges = rangesExtBb;
+var separator = ",";
+function expect(result, codepoint) {
+  return  "%26%23" + codepoint + "%3B";
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-han.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-han.html
index 0698ed5..82930e2 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-han.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-han.html
@@ -4,8 +4,24 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding errors (form, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,175 +34,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-  // set up a simple array of miscellaneous unicode codepoints that are not encoded
-  var codepoints = [];
-
-  for (i = 0x4e00; i < 0x9fba; i++) {
-    result = big5Encoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "cjk ";
-    }
-  }
-
-  for (i = 0xf900; i < 0xfa6e; i++) {
-    // compatibility
-    result = big5Encoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "compatibility ";
-    }
-  }
-
-  for (i = 0xfa70; i < 0xfada; i++) {
-    result = big5Encoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "compatibility ";
-    }
-  }
-
-  for (i = 0x3400; i < 0x4dbf; i++) {
-    // cjk extension A
-    result = big5Encoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "extension A ";
-    }
-  }
-  /*
-  for (i=0x20000;i<0x2A6E0;i++) { // cjk extension A
-    result = getByteSequence(i)
-    if (!result) {
-      var item = {}
-      codepoints.push(item)
-      item.cp = i
-      item.expected = '%26%23'+item.cp+'%3B'
-      item.desc = 'extension A '
-      }
-    }
-   */
-
-  // convert the information into a simple array of objects that can be easily traversed
-  var currentChunk = [];
-  var currentTests = [];
-  cplist = [currentChunk];
-  tests = [currentTests];
-  for (i = 0; i < codepoints.length; i++) {
-    if (currentChunk.length == chunkSize) {
-      currentChunk = [];
-      cplist.push(currentChunk);
-      currentTests = [];
-      tests.push(currentTests);
-    }
-    var item = {};
-    currentChunk.push(item);
-    item.cp = codepoints[i].cp;
-    item.expected = codepoints[i].expected;
-    item.desc = codepoints[i].desc;
-    currentTests.push(
-      async_test(
-        item.desc +
-          "U+" +
-          item.cp.toString(16).toUpperCase() +
-          " " +
-          String.fromCodePoint(item.cp) +
-          " " +
-          item.expected
-      )
-    );
-  }
-
-  numChunks = cplist.length;
-
-  for (var i = 0; i < numFrames; i++) {
-    var frame = document.createElement("iframe");
-    frame.id = frame.name = "frame-" + i;
-    document.body.appendChild(frame);
-    var form = document.createElement("form");
-    form.id = "form-" + i;
-    form.method = "GET";
-    form.action = "/common/blank.html";
-    form.acceptCharset = "big5";
-    form.target = frame.id;
-    var input = document.createElement("input");
-    input.id = input.name = "input-" + i;
-    form.appendChild(input);
-    document.body.appendChild(form);
-  }
-
-  addEventListener("load", function() {
-    frames = Array.prototype.slice.call(
-      document.getElementsByTagName("iframe")
-    );
-    forms = Array.prototype.slice.call(document.getElementsByTagName("form"));
-    inputs = Array.prototype.slice.call(document.getElementsByTagName("input"));
-    for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-      runNext(i);
-    }
-  });
-});
-
-function runNext(id) {
-  var i = currentChunkIndex;
-  currentChunkIndex += 1;
-
-  var iframe = frames[id];
-  var form = forms[id];
-  var input = inputs[id];
-
-  input.value = cplist[i]
-    .map(function(x) {
-      return String.fromCodePoint(x.cp);
-    })
-    .join(seperator);
-  form.submit();
-
-  iframe.onload = function() {
-    var url = iframe.contentWindow.location;
-    var query = url.search;
-    var result_string = query.substr(query.indexOf("=") + 1);
-    var results = result_string.split(encodedSeperator);
-
-    for (var j = 0; j < cplist[i].length; j++) {
-      var t = tests[i][j];
-      t.step(function() {
-        assert_equals(results[j], cplist[i][j].expected);
-      });
-      t.done();
-    }
-    if (currentChunkIndex < numChunks) {
-      runNext(id);
-    }
-  };
+var errors = true;
+var encoder = big5Encoder;
+var ranges = rangesHan;
+var separator = ",";
+function expect(result, codepoint) {
+  return  "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-hangul.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-hangul.html
index 106d9d1..ca90edf 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-hangul.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-hangul.html
@@ -4,8 +4,21 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding errors (form, hangul)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,132 +31,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 400;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of miscellaneous unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (i = 0xac00; i < 0xd7af; i++) {
-        result = big5Encoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "hangul ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "big5";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = big5Encoder;
+var ranges = rangesHangul;
+var separator = ",";
+function expect(result, codepoint) {
+  return  "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-misc.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-misc.html
index 98d6861..34d90c1 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-misc.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-misc.html
@@ -4,8 +4,13 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding errors (form, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,172 +23,20 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-  // set up a simple array of miscellaneous unicode codepoints that are not encoded
-  var codepoints = [];
-
-  for (var i = 0x80; i < 0x4ff; i++) {
-    result = big5Encoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "latin, greek, cyrillic, etc ";
-    }
-  }
-
-  for (i = 0x2000; i < 0x23ff; i++) {
-    result = big5Encoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "punctuation, currency, symbols ";
-    }
-  }
-
-  for (i = 0x2460; i < 0x26ff; i++) {
-    result = big5Encoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "enclosed chars and boxes ";
-    }
-  }
-
-  for (i = 0x3000; i < 0x33ff; i++) {
-    result = big5Encoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "various asian ";
-    }
-  }
-
-  for (i = 0xff00; i < 0xffef; i++) {
-    result = big5Encoder(String.fromCodePoint(i));
-    if (!result) {
-      var item = {};
-      codepoints.push(item);
-      item.cp = i;
-      item.expected = "%26%23" + item.cp + "%3B";
-      item.desc = "half/full width ";
-    }
-  }
-
-  // convert the information into a simple array of objects that can be easily traversed
-  var currentChunk = [];
-  var currentTests = [];
-  cplist = [currentChunk];
-  tests = [currentTests];
-  for (i = 0; i < codepoints.length; i++) {
-    if (currentChunk.length == chunkSize) {
-      currentChunk = [];
-      cplist.push(currentChunk);
-      currentTests = [];
-      tests.push(currentTests);
-    }
-    var item = {};
-    currentChunk.push(item);
-    item.cp = codepoints[i].cp;
-    item.expected = codepoints[i].expected;
-    item.desc = codepoints[i].desc;
-    currentTests.push(
-      async_test(
-        item.desc +
-          "U+" +
-          item.cp.toString(16).toUpperCase() +
-          " " +
-          String.fromCodePoint(item.cp) +
-          " " +
-          item.expected
-      )
-    );
-  }
-
-  numChunks = cplist.length;
-
-  for (var i = 0; i < numFrames; i++) {
-    var frame = document.createElement("iframe");
-    frame.id = frame.name = "frame-" + i;
-    document.body.appendChild(frame);
-    var form = document.createElement("form");
-    form.id = "form-" + i;
-    form.method = "GET";
-    form.action = "/common/blank.html";
-    form.acceptCharset = "big5";
-    form.target = frame.id;
-    var input = document.createElement("input");
-    input.id = input.name = "input-" + i;
-    form.appendChild(input);
-    document.body.appendChild(form);
-  }
-
-  addEventListener("load", function() {
-    frames = Array.prototype.slice.call(
-      document.getElementsByTagName("iframe")
-    );
-    forms = Array.prototype.slice.call(document.getElementsByTagName("form"));
-    inputs = Array.prototype.slice.call(document.getElementsByTagName("input"));
-    for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-      runNext(i);
-    }
-  });
-});
-
-function runNext(id) {
-  var i = currentChunkIndex;
-  currentChunkIndex += 1;
-
-  var iframe = frames[id];
-  var form = forms[id];
-  var input = inputs[id];
-
-  input.value = cplist[i]
-    .map(function(x) {
-      return String.fromCodePoint(x.cp);
-    })
-    .join(seperator);
-  form.submit();
-
-  iframe.onload = function() {
-    var url = iframe.contentWindow.location;
-    var query = url.search;
-    var result_string = query.substr(query.indexOf("=") + 1);
-    var results = result_string.split(encodedSeperator);
-
-    for (var j = 0; j < cplist[i].length; j++) {
-      var t = tests[i][j];
-      t.step(function() {
-        assert_equals(results[j], cplist[i][j].expected);
-      });
-      t.done();
-    }
-    if (currentChunkIndex < numChunks) {
-      runNext(id);
-    }
-  };
+var errors = true;
+var encoder = big5Encoder;
+var ranges = rangesMisc;
+var separator = ",";
+function expect(result, codepoint) {
+  return  "%26%23" + codepoint + "%3B";
+}
+// Overwrite normalizeStr
+function normalizeStr(str) {
+  return str;
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-pua.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-pua.html
index fd037d1..2fc7bb2 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-pua.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-pua.html
@@ -4,8 +4,16 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding errors (form, pua)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,132 +26,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // set up a simple array of miscellaneous unicode codepoints that are not encoded
-    var codepoints = [];
-
-    for (i = 0xe000; i < 0xf8ff; i++) {
-        result = big5Encoder(String.fromCodePoint(i));
-        if (!result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%26%23" + item.cp + "%3B";
-            item.desc = "pua ";
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        item.desc = codepoints[i].desc;
-        currentTests.push(
-            async_test(
-                item.desc +
-                    "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "big5";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(results[j], cplist[i][j].expected);
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = true;
+var encoder = big5Encoder;
+var ranges = rangesPua;
+var separator = ",";
+function expect(result, codepoint) {
+  return  "%26%23" + codepoint + "%3B";
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form-x-x-big5.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form-x-x-big5.html
index 79d2975..684dc52 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form-x-x-big5.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form-x-x-big5.html
@@ -4,8 +4,24 @@
 <meta charset="x-x-big5"> <!-- test breaks if the server overrides this -->
 <title>x-x-big5 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -17,133 +33,17 @@
 </style>
 </head>
 <body>
-
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0x2a6df; i++) {
-        result = big5Encoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "x-x-big5";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = big5Encoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-form.html b/encoding/legacy-mb-tchinese/big5/big5-encode-form.html
index b374fe5..2d72031 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-form.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-form.html
@@ -4,8 +4,24 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding (form)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
@@ -18,131 +34,16 @@
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
 <script>
-var tests = [];
-var cplist = [];
-var numTests = null;
-var numFrames = 2;
-var chunkSize = 500;
-var numChunks = null;
-var frames = null;
-var frames = null;
-var forms = null;
-var seperator = ",";
-var encodedSeperator = encodeURIComponent(",");
-var currentChunkIndex = 0;
-
-setup(function() {
-    // create a simple list of just those code points for which there is an encoding possible
-    codepoints = [];
-    for (var i = 0x80; i < 0x2a6df; i++) {
-        result = big5Encoder(String.fromCodePoint(i));
-        if (result) {
-            var item = {};
-            codepoints.push(item);
-            item.cp = i;
-            item.expected = "%" + result.replace(/ /g, "%");
-        }
-    }
-
-    // convert the information into a simple array of objects that can be easily traversed
-    var currentChunk = [];
-    var currentTests = [];
-    cplist = [currentChunk];
-    tests = [currentTests];
-    for (i = 0; i < codepoints.length; i++) {
-        if (currentChunk.length == chunkSize) {
-            currentChunk = [];
-            cplist.push(currentChunk);
-            currentTests = [];
-            tests.push(currentTests);
-        }
-        var item = {};
-        currentChunk.push(item);
-        item.cp = codepoints[i].cp;
-        item.expected = codepoints[i].expected;
-        currentTests.push(
-            async_test(
-                "U+" +
-                    item.cp.toString(16).toUpperCase() +
-                    " " +
-                    String.fromCodePoint(item.cp) +
-                    " " +
-                    item.expected
-            )
-        );
-    }
-
-    numChunks = cplist.length;
-
-    for (var i = 0; i < numFrames; i++) {
-        var frame = document.createElement("iframe");
-        frame.id = frame.name = "frame-" + i;
-        document.body.appendChild(frame);
-        var form = document.createElement("form");
-        form.id = "form-" + i;
-        form.method = "GET";
-        form.action = "/common/blank.html";
-        form.acceptCharset = "big5";
-        form.target = frame.id;
-        var input = document.createElement("input");
-        input.id = input.name = "input-" + i;
-        form.appendChild(input);
-        document.body.appendChild(form);
-    }
-
-    addEventListener("load", function() {
-        frames = Array.prototype.slice.call(
-            document.getElementsByTagName("iframe")
-        );
-        forms = Array.prototype.slice.call(
-            document.getElementsByTagName("form")
-        );
-        inputs = Array.prototype.slice.call(
-            document.getElementsByTagName("input")
-        );
-        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
-            runNext(i);
-        }
-    });
-});
-
-function runNext(id) {
-    var i = currentChunkIndex;
-    currentChunkIndex += 1;
-
-    var iframe = frames[id];
-    var form = forms[id];
-    var input = inputs[id];
-
-    input.value = cplist[i]
-        .map(function(x) {
-            return String.fromCodePoint(x.cp);
-        })
-        .join(seperator);
-    form.submit();
-
-    iframe.onload = function() {
-        var url = iframe.contentWindow.location;
-        var query = url.search;
-        var result_string = query.substr(query.indexOf("=") + 1);
-        var results = result_string.split(encodedSeperator);
-
-        for (var j = 0; j < cplist[i].length; j++) {
-            var t = tests[i][j];
-            t.step(function() {
-                assert_equals(
-                    normalizeStr(results[j]),
-                    normalizeStr(cplist[i][j].expected)
-                );
-            });
-            t.done();
-        }
-        if (currentChunkIndex < numChunks) {
-            runNext(id);
-        }
-    };
+var errors = false;
+var encoder = big5Encoder;
+var ranges = rangesAll;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
 }
 </script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-han.html b/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-han.html
index c538cf4..84d8b2d 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-han.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-han.html
@@ -4,97 +4,56 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding errors (href, han)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-11000">
+<meta name="variant" content="?11001-12000">
+<meta name="variant" content="?12001-13000">
+<meta name="variant" content="?13001-14000">
+<meta name="variant" content="?14001-15000">
+<meta name="variant" content="?15001-16000">
+<meta name="variant" content="?16001-17000">
+<meta name="variant" content="?17001-18000">
+<meta name="variant" content="?18001-19000">
+<meta name="variant" content="?19001-20000">
+<meta name="variant" content="?20001-21000">
+<meta name="variant" content="?21001-22000">
+<meta name="variant" content="?22001-23000">
+<meta name="variant" content="?23001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#big5">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding han characters that are not in the gbk encoding.">
-<script>
-function encode(input, expected, desc) {
-	// tests whether a Unicode character is converted to an equivalent byte sequence by href
-	// input: a Unicode character
-	// expected: expected byte sequence
-	// desc: what's being tested
-	test(function() {
-		var a = document.createElement("a"); // <a> uses document encoding for URL's query
-		a.href = "https://example.com/?" + input;
-		result = a.search.substr(1); // remove leading "?"
-		assert_equals(result, expected);
-	}, desc);
-}
-
-// create a simple list of just those code points for which there is no encoding is possible, from a representative subset of the BMP
-codepoints = [];
-
-for (i = 0x4e00; i < 0x9fba; i++) {
-	result = big5Encoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "cjk ";
-	}
-}
-
-for (i = 0xf900; i < 0xfa6e; i++) {
-	// compatibility
-	result = big5Encoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "compatibility ";
-	}
-}
-
-for (i = 0xfa70; i < 0xfada; i++) {
-	result = big5Encoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "compatibility ";
-	}
-}
-
-for (i = 0x3400; i < 0x4dbf; i++) {
-	// cjk extension A
-	result = big5Encoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "extension A ";
-	}
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-	encode(
-		String.fromCodePoint(codepoints[x].cp),
-		codepoints[x].expected,
-		codepoints[x].desc +
-			" U+" +
-			codepoints[x].cp.toString(16).toUpperCase() +
-			" " +
-			String.fromCodePoint(codepoints[x].cp) +
-			" " +
-			codepoints[x].expected
-	);
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the test excludes ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = big5Encoder;
+var ranges = rangesHan;
+var separator = ",";
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+</script>
+<script src="../../resources/encode-href-common.js"></script>
 </body>
 </html>
+
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-hangul.html b/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-hangul.html
index 48a23f0..d825447 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-hangul.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-hangul.html
@@ -4,62 +4,43 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding errors (href, hangul)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-8000">
+<meta name="variant" content="?8001-9000">
+<meta name="variant" content="?9001-10000">
+<meta name="variant" content="?10001-10000">
+<meta name="variant" content="?11001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#big5">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding hangul characters that are not in the gbk encoding.">
-<script>
-function encode(input, expected, desc) {
-  // tests whether a Unicode character is converted to an equivalent byte sequence by href
-  // input: a Unicode character
-  // expected: expected byte sequence
-  // desc: what's being tested
-  test(function() {
-    var a = document.createElement("a"); // <a> uses document encoding for URL's query
-    a.href = "https://example.com/?" + input;
-    result = a.search.substr(1); // remove leading "?"
-    assert_equals(result, expected);
-  }, desc);
-}
-
-// create a simple list of just those code points for which there is no encoding is possible, from a representative subset of the BMP
-codepoints = [];
-
-for (i = 0xac00; i < 0xd7af; i++) {
-  result = big5Encoder(String.fromCodePoint(i));
-  if (!result) {
-    var item = {};
-    codepoints.push(item);
-    item.cp = i;
-    item.expected = "%26%23" + item.cp + "%3B";
-    item.desc = "hangul ";
-  }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-  encode(
-    String.fromCodePoint(codepoints[x].cp),
-    codepoints[x].expected,
-    codepoints[x].desc +
-      " U+" +
-      codepoints[x].cp.toString(16).toUpperCase() +
-      " " +
-      String.fromCodePoint(codepoints[x].cp) +
-      " " +
-      codepoints[x].expected
-  );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the test excludes ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = big5Encoder;
+var ranges = rangesHangul;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+</script>
+<script src="../../resources/encode-href-common.js"></script>
 </body>
 </html>
+
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-misc.html b/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-misc.html
index 0ff27f4..1a212d6 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-misc.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-misc.html
@@ -4,105 +4,34 @@
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
 <title>Big5 encoding errors (href, misc)</title>
 <meta name="timeout" content="long">
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#big5">
 <meta name="assert" content="The browser produces percent-escaped character references when writing characters to an href value and encoding miscellaneous characters that are not in the gbk encoding.">
-<script>
-function encode(input, expected, desc) {
-	// tests whether a Unicode character is converted to an equivalent byte sequence by href
-	// input: a Unicode character
-	// expected: expected byte sequence
-	// desc: what's being tested
-	test(function() {
-		var a = document.createElement("a"); // <a> uses document encoding for URL's query
-		a.href = "https://example.com/?" + input;
-		result = a.search.substr(1); // remove leading "?"
-		assert_equals(result, expected);
-	}, desc);
-}
-
-// create a simple list of just those code points for which there is no encoding is possible, from a representative subset of the BMP
-codepoints = [];
-
-for (var i = 0x80; i < 0x4ff; i++) {
-	result = big5Encoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "latin, greek, cyrillic, etc ";
-	}
-}
-for (i = 0x2000; i < 0x23ff; i++) {
-	result = big5Encoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "punctuation, currency, symbols ";
-	}
-}
-
-for (i = 0x2460; i < 0x26ff; i++) {
-	result = big5Encoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "enclosed chars and boxes ";
-	}
-}
-
-for (i = 0x3000; i < 0x33ff; i++) {
-	result = big5Encoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "various asian ";
-	}
-}
-
-for (i = 0xff00; i < 0xffef; i++) {
-	result = big5Encoder(String.fromCodePoint(i));
-	if (!result) {
-		var item = {};
-		codepoints.push(item);
-		item.cp = i;
-		item.expected = "%26%23" + item.cp + "%3B";
-		item.desc = "half/full width ";
-	}
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-	encode(
-		String.fromCodePoint(codepoints[x].cp),
-		codepoints[x].expected,
-		codepoints[x].desc +
-			" U+" +
-			codepoints[x].cp.toString(16).toUpperCase() +
-			" " +
-			String.fromCodePoint(codepoints[x].cp) +
-			" " +
-			codepoints[x].expected
-	);
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the test excludes ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = true;
+var encoder = eucjpEncoder;
+var ranges = rangesMisc;
+function expect(result, codepoint) {
+  return "%26%23" + codepoint + "%3B";
+}
+</script>
+<script src="../../resources/encode-form-common.js"></script>
 </body>
 </html>
diff --git a/encoding/legacy-mb-tchinese/big5/big5-encode-href.html b/encoding/legacy-mb-tchinese/big5/big5-encode-href.html
index fb8824c..c7a23d8 100644
--- a/encoding/legacy-mb-tchinese/big5/big5-encode-href.html
+++ b/encoding/legacy-mb-tchinese/big5/big5-encode-href.html
@@ -4,59 +4,38 @@
 <meta name="timeout" content="long">
 <title>Big5 encoding (href)</title>
 <meta charset="big5"> <!-- test breaks if the server overrides this -->
+<meta name="variant" content="?1-1000">
+<meta name="variant" content="?1001-2000">
+<meta name="variant" content="?2001-3000">
+<meta name="variant" content="?3001-4000">
+<meta name="variant" content="?4001-5000">
+<meta name="variant" content="?5001-6000">
+<meta name="variant" content="?6001-7000">
+<meta name="variant" content="?7001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script src="big5_index.js"></script>
 <script src="big5-encoder.js"></script>
 <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
 <link rel="help" href="https://encoding.spec.whatwg.org/#big5">
 <meta name="assert" content="The browser produces the expected byte sequences for all characters in the big5 encoding after 0x9F when writing characters to an href value, using the encoder steps in the specification.">
-<script>
-function encode(input, expected, desc) {
-  // tests whether a Unicode character is converted to an equivalent byte sequence by href
-  // input: a Unicode character
-  // expected: expected byte sequence
-  // desc: what's being tested
-  test(function() {
-    var a = document.createElement("a"); // <a> uses document encoding for URL's query
-    a.href = "https://example.com/?" + input;
-    result = a.search.substr(1); // remove leading "?"
-    assert_equals(normalizeStr(result), normalizeStr(expected));
-  }, desc);
-}
-
-// create a simple list of just those code points for which there is an encoding possible
-codepoints = [];
-for (var i = 0x80; i < 0x2a6df; i++) {
-  result = big5Encoder(String.fromCodePoint(i));
-  if (result) {
-    var item = {};
-    codepoints.push(item);
-    item.cp = i;
-    item.expected = "%" + result.replace(/ /g, "%");
-  }
-}
-
-// run the tests
-for (var x = 0; x < codepoints.length; x++) {
-  encode(
-    String.fromCodePoint(codepoints[x].cp),
-    codepoints[x].expected,
-    "U+" +
-      codepoints[x].cp.toString(16).toUpperCase() +
-      " " +
-      String.fromCodePoint(codepoints[x].cp) +
-      " " +
-      codepoints[x].expected
-  );
-}
-
-// NOTES
-// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
-// the test excludes ASCII characters
-</script>
+<style>
+ iframe { display:none }
+ form { display:none }
+</style>
 </head>
 <body>
 <div id="log"></div>
+<script src="../../resources/ranges.js"></script>
+<script>
+var errors = false;
+var encoder = big5Encoder;
+var ranges = rangesAll;
+function expect(result, codepoint) {
+  return "%" + result.replace(/ /g, "%");
+}
+</script>
+<script src="../../resources/encode-href-common.js"></script>
 </body>
 </html>
diff --git a/encoding/resources/decode-common.js b/encoding/resources/decode-common.js
new file mode 100644
index 0000000..19dd693
--- /dev/null
+++ b/encoding/resources/decode-common.js
@@ -0,0 +1,40 @@
+var tests = [];
+
+function iframeRef(frameRef) {
+    return frameRef.contentWindow
+        ? frameRef.contentWindow.document
+        : frameRef.contentDocument;
+}
+
+function showNodes(decoder) {
+    var iframe = iframeRef(document.getElementById("scrwin"));
+    nodes = iframe.querySelectorAll("span");
+
+    for (var i = 0; i < nodes.length; i++) {
+        var test = subsetTest(async_test,
+                              "U+" +
+                              nodes[i].dataset.cp +
+                              " " +
+                              String.fromCodePoint(parseInt(nodes[i].dataset.cp, 16)) +
+                              " " +
+                              decoder(nodes[i].dataset.bytes) +
+                              " " +
+                              nodes[i].dataset.bytes
+        );
+        if (test) {
+            tests[i] = test;
+        }
+    }
+
+    for (var i = 0; i < nodes.length; i++) {
+        if (tests[i]) {
+            tests[i].step(function() {
+                assert_equals(
+                    nodes[i].textContent,
+                    decoder(nodes[i].dataset.bytes)
+                );
+            });
+            tests[i].done();
+        }
+    }
+}
diff --git a/encoding/resources/encode-form-common.js b/encoding/resources/encode-form-common.js
new file mode 100644
index 0000000..6f8777b
--- /dev/null
+++ b/encoding/resources/encode-form-common.js
@@ -0,0 +1,140 @@
+// These are defined by the test:
+// errors (boolean)
+// encoder (function)
+// ranges (array)
+// separator (string)
+// expect (function)
+
+var tests = [];
+var cplist = [];
+var numTests = null;
+var numFrames = 2;
+var chunkSize = 400;
+var numChunks = null;
+var frames = null;
+var frames = null;
+var forms = null;
+var encodedSeparator = encodeURIComponent(separator);
+var currentChunkIndex = 0;
+var pageCharset = document.querySelector("meta[charset]").getAttribute("charset");
+
+setup(function() {
+    // create a simple list of just those code points for which there is an encoding possible
+    codepoints = [];
+    for (var range of ranges) {
+        for (var i = range[0]; i < range[1]; i++) {
+            result = encoder(String.fromCodePoint(i));
+            var success = !!result;
+            if (errors) {
+              success = !success;
+            }
+            if (success) {
+                var item = {};
+                codepoints.push(item);
+                item.cp = i;
+                item.expected = expect(result, i);
+                item.desc = range[2];
+            }
+        }
+    }
+
+    // convert the information into a simple array of objects that can be easily traversed
+    var currentChunk = [];
+    var currentTests = [];
+    cplist = [currentChunk];
+    tests = [currentTests];
+    for (i = 0; i < codepoints.length; i++) {
+        if (currentChunk.length == chunkSize) {
+            currentChunk = [];
+            cplist.push(currentChunk);
+            currentTests = [];
+            tests.push(currentTests);
+        }
+        var item = {};
+        currentChunk.push(item);
+        item.cp = codepoints[i].cp;
+        item.expected = codepoints[i].expected;
+        item.desc = codepoints[i].desc;
+        currentTests.push(subsetTest(async_test,
+                                     (item.desc ? item.desc + " " : "") +
+                                     "U+" +
+                                     item.cp.toString(16).toUpperCase() +
+                                     " " +
+                                     String.fromCodePoint(item.cp) +
+                                     " " +
+                                     item.expected
+        ));
+    }
+
+    numChunks = cplist.length;
+
+    for (var i = 0; i < numFrames; i++) {
+        var frame = document.createElement("iframe");
+        frame.id = frame.name = "frame-" + i;
+        document.body.appendChild(frame);
+        var form = document.createElement("form");
+        form.id = "form-" + i;
+        form.method = "GET";
+        form.action = "/common/blank.html";
+        form.acceptCharset = pageCharset;
+        form.target = frame.id;
+        var input = document.createElement("input");
+        input.id = input.name = "input-" + i;
+        form.appendChild(input);
+        document.body.appendChild(form);
+    }
+
+    addEventListener("load", function() {
+        frames = Array.prototype.slice.call(
+            document.getElementsByTagName("iframe")
+        );
+        forms = Array.prototype.slice.call(
+            document.getElementsByTagName("form")
+        );
+        inputs = Array.prototype.slice.call(
+            document.getElementsByTagName("input")
+        );
+        for (var i = 0; i < Math.min(numFrames, numChunks); i++) {
+            runNext(i);
+        }
+    });
+});
+
+function runNext(id) {
+    var i = currentChunkIndex;
+    currentChunkIndex += 1;
+
+    var iframe = frames[id];
+    var form = forms[id];
+    var input = inputs[id];
+
+    input.value = cplist[i]
+        .map(function(x) {
+            return String.fromCodePoint(x.cp);
+        })
+        .join(separator);
+    form.submit();
+
+    iframe.onload = function() {
+        var url = iframe.contentWindow.location;
+        var query = url.search;
+        var result_string = query.substr(query.indexOf("=") + 1);
+        var results = result_string.split(encodedSeparator);
+
+        for (var j = 0; j < cplist[i].length; j++) {
+            var t = tests[i][j];
+            if (t) {
+                t.step(function() {
+                    assert_equals(
+                        normalizeStr(results[j]),
+                        normalizeStr(cplist[i][j].expected)
+                    );
+                });
+                t.done();
+            }
+        }
+        if (currentChunkIndex < numChunks) {
+            runNext(id);
+        }
+    };
+}
diff --git a/encoding/resources/encode-href-common.js b/encoding/resources/encode-href-common.js
new file mode 100644
index 0000000..dc646fe
--- /dev/null
+++ b/encoding/resources/encode-href-common.js
@@ -0,0 +1,57 @@
+// These are defined by the test:
+// errors (boolean)
+// encoder (function)
+// ranges (array)
+// expect (function)
+
+function encode(input, expected, desc) {
+    // tests whether a Unicode character is converted to an equivalent byte sequence by href
+    // input: a Unicode character
+    // expected: expected byte sequence
+    // desc: what's being tested
+    subsetTest(test, function() {
+        var a = document.createElement("a"); // <a> uses document encoding for URL's query
+        a.href = "https://example.com/?" + input;
+        result = a.search.substr(1); // remove leading "?"
+        assert_equals(normalizeStr(result), normalizeStr(expected));
+    }, desc);
+}
+
+// set up a simple array of unicode codepoints that are not encoded
+var codepoints = [];
+
+for (var range of ranges) {
+    for (var i = range[0]; i < range[1]; i++) {
+        result = encoder(String.fromCodePoint(i));
+        var success = !!result;
+        if (errors) {
+          success = !success;
+        }
+        if (success) {
+            var item = {};
+            codepoints.push(item);
+            item.cp = i;
+            item.expected = expect(result, i);
+            item.desc = range[2] ? range[2] + " " : "";
+        }
+    }
+}
+
+// run the tests
+for (var x = 0; x < codepoints.length; x++) {
+    encode(
+        String.fromCodePoint(codepoints[x].cp),
+        codepoints[x].expected,
+        codepoints[x].desc +
+            " U+" +
+            codepoints[x].cp.toString(16).toUpperCase() +
+            " " +
+            String.fromCodePoint(codepoints[x].cp) +
+            " " +
+            codepoints[x].expected
+    );
+}
+
+// NOTES
+// this test relies on support for String.fromCodePoint, which appears to be supported by major desktop browsers
+// the tests exclude ASCII characters
diff --git a/encoding/resources/ranges.js b/encoding/resources/ranges.js
new file mode 100644
index 0000000..81dc711
--- /dev/null
+++ b/encoding/resources/ranges.js
@@ -0,0 +1,28 @@
+var rangesHan = [
+  [0x4e00, 0x9fba, "cjk"],
+  [0xf900, 0xfa6e, "compatibility"],
+  [0xfa70, 0xfada, "compatibility"],
+  [0x3400, 0x4dbf, "extension A"],
+];
+var rangesHangul = [
+  [0xac00, 0xd7af, "hangul"],
+];
+var rangesMisc = [
+  [0x80, 0x4ff, "latin, greek, cyrillic, etc"],
+  [0x2000, 0x23ff, "punctuation, currency, symbols"],
+  [0x2460, 0x26ff, "enclosed chars and boxes"],
+  [0x3000, 0x33ff, "various asian"],
+  [0xff00, 0xffef, "half/full width"],
+];
+var rangesAll = [
+  [0x80, 0xffff],
+];
+var rangesExtBa = [
+  [0x20000, 0x2536b, "extB (pt 1)"],
+];
+var rangesExtBb = [
+  [0x2536b, 0x2a6e0, "extB (pt 2)"],
+];
+var rangesPua = [
+  [0xe000, 0xf8ff, "pua"],
+];
diff --git a/encoding/single-byte-decoder.html b/encoding/single-byte-decoder.html
index 1833fda..70d8fb7 100644
--- a/encoding/single-byte-decoder.html
+++ b/encoding/single-byte-decoder.html
@@ -1,5 +1,8 @@
 <!doctype html>
 <meta name=timeout content=long>
+<meta name="variant" content="?XMLHttpRequest">
+<meta name="variant" content="?TextDecoder">
+<meta name="variant" content="?document">
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src=resources/encodings.js></script>
@@ -62,38 +65,49 @@
    }
  }
 
+ var subsetTest = "";
+ if (location.search) {
+   subsetTest = location.search.substr(1);
+ }
+
  // Setting up all the tests
  for(var i = 0, l = singleByteEncodings.length; i < l; i++) {
    var encoding = singleByteEncodings[i]
    for(var ii = 0, ll = encoding.labels.length; ii < ll; ii++) {
      var label = encoding.labels[ii]
 
-     async_test(function(t) {
-       var xhr = new XMLHttpRequest,
-           name = encoding.name // need scoped variable
-       xhr.open("GET", "resources/single-byte-raw.py?label=" + label)
-       xhr.send(null)
-       xhr.onload = t.step_func_done(function() { assert_decode(xhr.responseText, name) })
-     }, encoding.name + ": " + label + " (XMLHttpRequest)")
+     if (subsetTest == "XMLHttpRequest" || !subsetTest) {
+       async_test(function(t) {
+         var xhr = new XMLHttpRequest,
+             name = encoding.name // need scoped variable
+         xhr.open("GET", "resources/single-byte-raw.py?label=" + label)
+         xhr.send(null)
+         xhr.onload = t.step_func_done(function() { assert_decode(xhr.responseText, name) })
+       }, encoding.name + ": " + label + " (XMLHttpRequest)")
+     }
 
-     test(function() {
-       var d = new TextDecoder(label),
-           data = d.decode(view)
-       assert_equals(d.encoding, encoding.name.toLowerCase()) // ASCII names only, so safe
-       assert_decode(data, encoding.name)
-     }, encoding.name + ": " + label + " (TextDecoder)")
+     if (subsetTest == "TextDecoder" || !subsetTest) {
+       test(function() {
+         var d = new TextDecoder(label),
+             data = d.decode(view)
+         assert_equals(d.encoding, encoding.name.toLowerCase()) // ASCII names only, so safe
+         assert_decode(data, encoding.name)
+       }, encoding.name + ": " + label + " (TextDecoder)")
+     }
 
-     async_test(function(t) {
-       var frame = document.createElement("iframe"),
-           name = encoding.name;
-       frame.src = "resources/text-plain-charset.py?label=" + label
-       frame.onload = t.step_func_done(function() {
-         assert_equals(frame.contentDocument.characterSet, name)
-         assert_equals(frame.contentDocument.inputEncoding, name)
-       })
-       t.add_cleanup(function() { document.body.removeChild(frame) })
-       document.body.appendChild(frame)
-     }, encoding.name + ": " + label + " (document.characterSet and document.inputEncoding)")
+     if (subsetTest == "document" || !subsetTest) {
+       async_test(function(t) {
+         var frame = document.createElement("iframe"),
+             name = encoding.name;
+         frame.src = "resources/text-plain-charset.py?label=" + label
+         frame.onload = t.step_func_done(function() {
+           assert_equals(frame.contentDocument.characterSet, name)
+           assert_equals(frame.contentDocument.inputEncoding, name)
+         })
+         t.add_cleanup(function() { document.body.removeChild(frame) })
+         document.body.appendChild(frame)
+       }, encoding.name + ": " + label + " (document.characterSet and document.inputEncoding)")
+     }
    }
  }
 </script>