]> granicus.if.org Git - icu/commitdiff
ICU-20329 XLocaleMatcher new test data format, parameterized test, more test cases
authorMarkus Scherer <markus.icu@gmail.com>
Fri, 4 Jan 2019 22:42:38 +0000 (14:42 -0800)
committerMarkus Scherer <markus.icu@gmail.com>
Wed, 9 Jan 2019 22:20:27 +0000 (14:20 -0800)
icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/XLocaleMatcherTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/data/localeMatcherTest.txt

index c84d8c0a2d042d4141d598a9675548fb3094aa4a..07c87b9968dff10e7ac3fb9801089ac9c3a5edc4 100644 (file)
@@ -3,38 +3,38 @@
 package com.ibm.icu.dev.test.util;
 
 
-import java.io.IOException;
+import java.io.BufferedReader;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 import java.util.Set;
 import java.util.TreeSet;
-import java.util.regex.Pattern;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 import com.ibm.icu.dev.test.TestFmwk;
-import com.ibm.icu.impl.locale.XCldrStub.Joiner;
-import com.ibm.icu.impl.locale.XCldrStub.Splitter;
+import com.ibm.icu.impl.locale.XCldrStub.FileUtilities;
 import com.ibm.icu.impl.locale.XLocaleDistance;
 import com.ibm.icu.impl.locale.XLocaleDistance.DistanceOption;
 import com.ibm.icu.impl.locale.XLocaleMatcher;
-import com.ibm.icu.text.UnicodeSet;
 import com.ibm.icu.util.LocaleMatcher;
 import com.ibm.icu.util.LocalePriorityList;
 import com.ibm.icu.util.Output;
 import com.ibm.icu.util.ULocale;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 /**
  * Test the XLocaleMatcher.
  *
  * @author markdavis
  */
-@RunWith(JUnit4.class)
+@RunWith(JUnitParamsRunner.class)
 public class XLocaleMatcherTest extends TestFmwk {
-    private static final boolean REFORMAT = false; // set to true to get a reformatted data file listed
-
     private static final int REGION_DISTANCE = 4;
 
     private static final XLocaleDistance LANGUAGE_MATCHER_DATA = XLocaleDistance.getDefault();
@@ -56,15 +56,6 @@ public class XLocaleMatcherTest extends TestFmwk {
         return XLocaleMatcher.builder().setSupportedLocales(string).setThresholdDistance(d).build();
     }
 
-    private XLocaleMatcher newXLocaleMatcher(LocalePriorityList string, int d, DistanceOption distanceOption) {
-        return XLocaleMatcher
-            .builder()
-            .setSupportedLocales(string)
-            .setThresholdDistance(d)
-            .setDistanceOption(distanceOption)
-            .build();
-    }
-
     //    public void testParentLocales() {
     //        // find all the regions that have a closer relation because of an explicit parent
     //        Set<String> explicitParents = new HashSet<>(INFO.getExplicitParents());
@@ -125,7 +116,7 @@ public class XLocaleMatcherTest extends TestFmwk {
     @Test
     public void testExactMatches() {
         String lastBase = "";
-        TreeSet<ULocale> sorted = new TreeSet<ULocale>();
+        TreeSet<ULocale> sorted = new TreeSet<>();
         for (ULocale loc : ULocale.getAvailableLocales()) {
             String language = loc.getLanguage();
             if (!lastBase.equals(language)) {
@@ -264,75 +255,214 @@ public class XLocaleMatcherTest extends TestFmwk {
         return (delta / iterations);
     }
 
-    @Test
-    public void testDataDriven() throws IOException {
-        DataDrivenTestHelper tfh = new MyTestFileHandler()
-            .setFramework(this)
-            .run(XLocaleMatcherTest.class, "data/localeMatcherTest.txt");
-        if (REFORMAT) {
-            System.out.println(tfh.appendLines(new StringBuilder()));
+    private static final class TestCase implements Cloneable {
+        private static final String ENDL = System.getProperties().getProperty("line.separator");
+
+        int lineNr = 0;
+
+        String nameLine = "";
+        String supportedLine = "";
+        String defaultLine = "";
+        String distanceLine = "";
+        String thresholdLine = "";
+        String matchLine = "";
+
+        String supported = "";
+        String def = "";
+        String distance = "";
+        String threshold = "";
+        String desired = "";
+        String expMatch = "";
+        String expDesired = "";
+        String expCombined = "";
+
+        @Override
+        public TestCase clone() throws CloneNotSupportedException {
+            return (TestCase) super.clone();
         }
-    }
 
-    private static final Splitter COMMA_SPACE = Splitter.on(Pattern.compile(",\\s*|\\s+")).trimResults();
-    private static final Joiner JOIN_COMMA_SPACE = Joiner.on(", ");
-    @SuppressWarnings("unused")
-    private static final UnicodeSet DIGITS = new UnicodeSet("[0-9]").freeze();
+        void reset(String newNameLine) {
+            nameLine = newNameLine;
+            supportedLine = "";
+            defaultLine = "";
+            distanceLine = "";
+            thresholdLine = "";
+
+            supported = "";
+            def = "";
+            distance = "";
+            threshold = "";
+        }
 
-    class MyTestFileHandler extends DataDrivenTestHelper {
+        String toInputsKey() {
+            return supported + '+' + def + '+' + distance + '+' + threshold + '+' + desired;
+        }
 
-        Output<ULocale> bestDesired = new Output<ULocale>();
-        DistanceOption distanceOption = DistanceOption.REGION_FIRST;
-        int threshold = -1;
+        private static void appendLine(StringBuilder sb, String line) {
+            if (!line.isEmpty()) {
+                sb.append(ENDL).append(line);
+            }
+        }
 
         @Override
-        public void handle(int lineNumber, boolean breakpoint, String commentBase, List<String> arguments) {
-            List<String> supported = COMMA_SPACE.splitToList(arguments.get(0));
-            final String supportedReformatted = JOIN_COMMA_SPACE.join(supported);
-            LocalePriorityList supportedList = LocalePriorityList.add(supportedReformatted).build();
+        public String toString() {
+            StringBuilder sb = new StringBuilder(nameLine);
+            appendLine(sb, supportedLine);
+            appendLine(sb, defaultLine);
+            appendLine(sb, distanceLine);
+            appendLine(sb, thresholdLine);
+            sb.append(ENDL).append("line ").append(lineNr).append(':');
+            appendLine(sb, matchLine);
+            return sb.toString();
+        }
+    }
 
-            Iterable<String> desired = COMMA_SPACE.split(arguments.get(1));
-            final String desiredReformatted = JOIN_COMMA_SPACE.join(desired);
-            LocalePriorityList desiredList = LocalePriorityList.add(desiredReformatted).build();
+    private static String getSuffixAfterPrefix(String s, int limit, String prefix) {
+        if (prefix.length() <= limit && s.startsWith(prefix)) {
+            return s.substring(prefix.length(), limit);
+        } else {
+            return null;
+        }
+    }
 
-            String expected = arguments.get(2);
-            String expectedLanguageTag = expected.equals("null") ? null : new ULocale(expected).toLanguageTag();
+    // UsedReflectively, not private to avoid unused-warning
+    static List<TestCase> readTestCases() throws Exception {
+        List<TestCase> tests = new ArrayList<>();
+        Map<String, Integer> uniqueTests = new HashMap<>();
+        TestCase test = new TestCase();
+        String filename = "data/localeMatcherTest.txt";
+        try (BufferedReader in = FileUtilities.openFile(XLocaleMatcherTest.class, filename)) {
+            String line;
+            while ((line = in.readLine()) != null) {
+                ++test.lineNr;
+                // Start of comment, or end of line, minus trailing spaces.
+                int limit = line.indexOf('#');
+                if (limit < 0) {
+                    limit = line.length();
+                }
+                char c;
+                while (limit > 0 && ((c = line.charAt(limit - 1)) == ' ' || c == '\t')) {
+                    --limit;
+                }
+                if (limit == 0) {  // empty line
+                    continue;
+                }
+                String suffix;
+                if (line.startsWith("** test: ")) {
+                    test.reset(line);
+                } else if ((suffix = getSuffixAfterPrefix(line, limit, "@supported=")) != null) {
+                    test.supportedLine = line;
+                    test.supported = suffix;
+                } else if ((suffix = getSuffixAfterPrefix(line, limit, "@default=")) != null) {
+                    test.defaultLine = line;
+                    test.def = suffix;
+                } else if ((suffix = getSuffixAfterPrefix(line, limit, "@distance=")) != null) {
+                    test.distanceLine = line;
+                    test.distance = suffix;
+                } else if ((suffix = getSuffixAfterPrefix(line, limit, "@threshold=")) != null) {
+                    test.thresholdLine = line;
+                    test.threshold = suffix;
+                } else {
+                    int matchSep = line.indexOf(">>");
+                    // >> before an inline comment, and followed by more than white space.
+                    if (0 <= matchSep && (matchSep + 2) < limit) {
+                        test.matchLine = line;
+                        test.desired = line.substring(0, matchSep).trim();
+                        test.expDesired = test.expCombined = "";
+                        int start = matchSep + 2;
+                        int expLimit = line.indexOf('|', start);
+                        if (expLimit < 0) {
+                            test.expMatch = line.substring(start, limit).trim();
+                        } else {
+                            test.expMatch = line.substring(start, expLimit).trim();
+                            start = expLimit + 1;
+                            expLimit = line.indexOf('|', start);
+                            if (expLimit < 0) {
+                                test.expDesired = line.substring(start, limit).trim();
+                            } else {
+                                test.expDesired = line.substring(start, expLimit).trim();
+                                test.expCombined = line.substring(expLimit + 1, limit).trim();
+                            }
+                        }
+                        String inputs = test.toInputsKey();
+                        Integer prevIndex = uniqueTests.get(inputs);
+                        if (prevIndex == null) {
+                            uniqueTests.put(inputs, tests.size());
+                        } else {
+                            System.out.println("Locale matcher test case on line " + test.lineNr
+                                    + " is a duplicate of line " + tests.get(prevIndex).lineNr);
+                        }
+                        tests.add(test.clone());
+                    } else {
+                        throw new IllegalArgumentException("test data syntax error on line "
+                                + test.lineNr + "\n" + line);
+                    }
+                }
+            }
+        }
+        System.out.println("Number of duplicate locale matcher test cases: " + (tests.size() - uniqueTests.size()));
+        return tests;
+    }
 
-            String expectedUi = arguments.size() < 4 ? null : arguments.get(3);
-            String expectedUiLanguageTag = expectedUi == null || expectedUi.equals("null") ? null
-                : new ULocale(expectedUi).toLanguageTag();
+    private static ULocale getULocaleOrNull(String s) {
+        if (s.equals("null")) {
+            return null;
+        } else {
+            return new ULocale(s);
+        }
+    }
 
-            if (breakpoint) {
-                breakpoint = false; // put debugger breakpoint here to break at @debug in test file
+    @Test
+    @Parameters(method = "readTestCases")
+    public void dataDriven(TestCase test) {
+        XLocaleMatcher matcher;
+        if (test.def.isEmpty() && test.distance.isEmpty() && test.threshold.isEmpty()) {
+            matcher = new XLocaleMatcher(test.supported);
+        } else {
+            XLocaleMatcher.Builder builder = XLocaleMatcher.builder();
+            builder.setSupportedLocales(test.supported);
+            if (!test.def.isEmpty()) {
+                builder.setDefaultLanguage(new ULocale(test.def));
             }
-            XLocaleMatcher matcher = threshold < 0 && distanceOption == DistanceOption.REGION_FIRST
-                ? newXLocaleMatcher(supportedList)
-                : newXLocaleMatcher(supportedList, threshold, distanceOption);
-            commentBase = "(" + lineNumber + ") " + commentBase;
-
-            ULocale bestSupported;
-            if (expectedUi != null) {
-                bestSupported = matcher.getBestMatch(desiredList, bestDesired);
-                ULocale bestUI = XLocaleMatcher.combine(bestSupported, bestDesired.value);
-                assertEquals(commentBase + " (UI)", expectedUiLanguageTag, bestUI == null ? null : bestUI.toLanguageTag());
-            } else {
-                bestSupported = matcher.getBestMatch(desiredList);
+            if (!test.distance.isEmpty()) {
+                DistanceOption distance;
+                switch (test.distance) {
+                case "normal":
+                    distance = DistanceOption.REGION_FIRST;
+                    break;
+                case "script":
+                    distance = DistanceOption.SCRIPT_FIRST;
+                    break;
+                default:
+                    throw new IllegalArgumentException("unsupported distance value " + test.distance);
+                }
+                builder.setDistanceOption(distance);
+            }
+            if (!test.threshold.isEmpty()) {
+                int threshold = Integer.valueOf(test.threshold);
+                builder.setThresholdDistance(threshold);
             }
-            String bestMatchLanguageTag = bestSupported == null ? null : bestSupported.toLanguageTag();
-            assertEquals(commentBase, expectedLanguageTag, bestMatchLanguageTag);
+            matcher = builder.build();
         }
 
-        @Override
-        public void handleParams(String comment, List<String> arguments) {
-            String switchItem = arguments.get(0);
-            if (switchItem.equals("@DistanceOption")) {
-                distanceOption = DistanceOption.valueOf(arguments.get(1));
-            } else if (switchItem.equals("@Threshold")) {
-                threshold = Integer.valueOf(arguments.get(1));
-            } else {
-                super.handleParams(comment, arguments);
+        ULocale expMatch = getULocaleOrNull(test.expMatch);
+        if (test.expDesired.isEmpty() && test.expCombined.isEmpty()) {
+            ULocale bestSupported = matcher.getBestMatch(test.desired);
+            assertEquals("bestSupported", expMatch, bestSupported);
+        } else {
+            LocalePriorityList desired = LocalePriorityList.add(test.desired).build();
+            Output<ULocale> bestDesired = new Output<>();
+            ULocale bestSupported = matcher.getBestMatch(desired, bestDesired);
+            assertEquals("bestSupported", expMatch, bestSupported);
+            if (!test.expDesired.isEmpty()) {
+                ULocale expDesired = getULocaleOrNull(test.expDesired);
+                assertEquals("bestDesired", expDesired, bestDesired.value);
+            }
+            if (!test.expCombined.isEmpty()) {
+                ULocale expCombined = getULocaleOrNull(test.expCombined);
+                ULocale combined = XLocaleMatcher.combine(bestSupported, bestDesired.value);
+                assertEquals("combined", expCombined, combined);
             }
-            return;
         }
     }
 }
index 55c0f3f5a962ae2f24f0fbb18f313b1bf5dd7648..3a149dd7c4031d1ab35ce84cffb1afe21bd75ecd 100644 (file)
 # © 2017 and later: Unicode, Inc. and others.
 # License & terms of use: http://www.unicode.org/copyright.html#License
 #
-# Data-driven test for the XLocaleMatcher.
-# Format
-# • Everything after "#" is a comment
-# • Arguments are separated by ";". They are:
-
-# supported ; desired ; expected
+# Data-driven test for the language/locale matcher.
+# Format:
+#
+# Everything after "#" is a comment.
+# ** test: This line starts a group of test cases.
+#
+# Lines starting with an '@' sign provide matcher parameters.
+# @supported=<comma-separated supported languages>
+# @default=<default language>  # no value = no explicit default
+# @distance=[normal|script]  # no value = no explicit setting
+# @threshold=<number 0..100>  # no value = no explicit setting
+#
+# A line with ">>" is a getBestMatch() test case:
+# <comma-separated desired languages> >> match | desired | combined
+# - match is the expected best supported language
+# - desired is the expected best desired language
+# - combined is the expected result of combine(match, desired)
+# An expected language can be "null" to check for the matcher returning null.
+# An empty or omitted value is not tested. (Omitted = not even the '|' separator.)
+#
+# ** test: A new test group resets all matcher parameters.
 
-# • The supported may have the threshold distance reset as a first item, eg 50, en, fr
-# A line starting with @debug will reach a statement in the test code where you can put a breakpoint for debugging
-# The test code also supports reformatting this file, by setting the REFORMAT flag.
+## X
 
-##################################################
-# testParentLocales
+** test: testParentLocales
 
 # es-419, es-AR, and es-MX are in a cluster; es is in a different one
 
-@debug
-es-419, es-ES ;        es-AR ;         es-419
-es-ES, es-419 ;        es-AR ;         es-419
+@supported=es-419, es-ES
+es-AR >> es-419
+@supported=es-ES, es-419
+es-AR >> es-419
 
-es-419, es ;   es-AR ;         es-419
-es, es-419 ;   es-AR ;         es-419
+@supported=es-419, es
+es-AR >> es-419
+@supported=es, es-419
+es-AR >> es-419
 
-es-MX, es ;    es-AR ;         es-MX
-es, es-MX ;    es-AR ;         es-MX
+@supported=es-MX, es
+es-AR >> es-MX
+@supported=es, es-MX
+es-AR >> es-MX
 
 # en-GB, en-AU, and en-NZ are in a cluster; en in a different one
 
-en-GB, en-US ;         en-AU ;         en-GB
-en-US, en-GB ;         en-AU ;         en-GB
+@supported=en-GB, en-US
+en-AU >> en-GB
+@supported=en-US, en-GB
+en-AU >> en-GB
 
-en-GB, en ;    en-AU ;         en-GB
-en, en-GB ;    en-AU ;         en-GB
+@supported=en-GB, en
+en-AU >> en-GB
+@supported=en, en-GB
+en-AU >> en-GB
 
-en-NZ, en-US ;         en-AU ;         en-NZ
-en-US, en-NZ ;         en-AU ;         en-NZ
+@supported=en-NZ, en-US
+en-AU >> en-NZ
+@supported=en-US, en-NZ
+en-AU >> en-NZ
 
-en-NZ, en ;    en-AU ;         en-NZ
-en, en-NZ ;    en-AU ;         en-NZ
+@supported=en-NZ, en
+en-AU >> en-NZ
+@supported=en, en-NZ
+en-AU >> en-NZ
 
 # pt-AU and pt-PT in one cluster; pt-BR in another
 
-pt-PT, pt-BR ;         pt-AO ;         pt-PT
-pt-BR, pt-PT ;         pt-AO ;         pt-PT
-
-pt-PT, pt ;    pt-AO ;         pt-PT
-pt, pt-PT ;    pt-AO ;         pt-PT
-
-zh-MO, zh-TW ;         zh-HK ;         zh-MO
-zh-TW, zh-MO ;         zh-HK ;         zh-MO
-
-zh-MO, zh-TW ;         zh-HK ;         zh-MO
-zh-TW, zh-MO ;         zh-HK ;         zh-MO
-
-zh-MO, zh-CN ;         zh-HK ;         zh-MO
-zh-CN, zh-MO ;         zh-HK ;         zh-MO
-
-zh-MO, zh ;    zh-HK ;         zh-MO
-zh, zh-MO ;    zh-HK ;         zh-MO
-
-##################################################
-# testChinese
-
-zh-CN, zh-TW, iw ;     zh-Hant-TW ;    zh-TW
-zh-CN, zh-TW, iw ;     zh-Hant ;       zh-TW
-zh-CN, zh-TW, iw ;     zh-TW ;         zh-TW
-zh-CN, zh-TW, iw ;     zh-Hans-CN ;    zh-CN
-zh-CN, zh-TW, iw ;     zh-CN ;         zh-CN
-zh-CN, zh-TW, iw ;     zh ;    zh-CN
-
-##################################################
-# testenGB
-
-fr, en, en-GB, es-419, es-MX, es ;     en-NZ ;         en-GB
-fr, en, en-GB, es-419, es-MX, es ;     es-ES ;         es
-fr, en, en-GB, es-419, es-MX, es ;     es-AR ;         es-419
-fr, en, en-GB, es-419, es-MX, es ;     es-MX ;         es-MX
-
-##################################################
-# testFallbacks
-
-91, en, hi ;   sa ;    hi
-
-##################################################
-# testBasics
-
-fr, en-GB, en ;        en-GB ;         en-GB
-fr, en-GB, en ;        en ;    en
-fr, en-GB, en ;        fr ;    fr
-fr, en-GB, en ;        ja ;    fr      # return first if no match
-
-##################################################
-# testFallback
+@supported=pt-PT, pt-BR
+pt-AO >> pt-PT
+@supported=pt-BR, pt-PT
+pt-AO >> pt-PT
+
+@supported=pt-PT, pt
+pt-AO >> pt-PT
+@supported=pt, pt-PT
+pt-AO >> pt-PT
+
+@supported=zh-MO, zh-TW
+zh-HK >> zh-MO
+@supported=zh-TW, zh-MO
+zh-HK >> zh-MO
+
+@supported=zh-MO, zh-CN
+zh-HK >> zh-MO
+@supported=zh-CN, zh-MO
+zh-HK >> zh-MO
+
+@supported=zh-MO, zh
+zh-HK >> zh-MO
+@supported=zh, zh-MO
+zh-HK >> zh-MO
+
+@distance=script
+@supported=es-419, es-ES
+es-AR >> es-419
+@supported=es-ES, es-419
+es-AR >> es-419
+@supported=es-419, es
+es-AR >> es-419
+@supported=es, es-419
+es-AR >> es-419
+@supported=es-MX, es
+es-AR >> es-MX
+@supported=es, es-MX
+es-AR >> es-MX
+@supported=en-GB, en-US
+en-AU >> en-GB
+@supported=en-US, en-GB
+en-AU >> en-GB
+@supported=en-GB, en
+en-AU >> en-GB
+@supported=en, en-GB
+en-AU >> en-GB
+@supported=en-NZ, en-US
+en-AU >> en-NZ
+@supported=en-US, en-NZ
+en-AU >> en-NZ
+@supported=en-NZ, en
+en-AU >> en-NZ
+@supported=en, en-NZ
+en-AU >> en-NZ
+@supported=pt-PT, pt-BR
+pt-AO >> pt-PT
+@supported=pt-BR, pt-PT
+pt-AO >> pt-PT
+@supported=pt-PT, pt
+pt-AO >> pt-PT
+@supported=pt, pt-PT
+pt-AO >> pt-PT
+@supported=zh-MO, zh-TW
+zh-HK >> zh-MO
+@supported=zh-TW, zh-MO
+zh-HK >> zh-MO
+@supported=zh-MO, zh-CN
+zh-HK >> zh-MO
+@supported=zh-CN, zh-MO
+zh-HK >> zh-MO
+@supported=zh-MO, zh
+zh-HK >> zh-MO
+@supported=zh, zh-MO
+zh-HK >> zh-MO
+
+** test: testChinese
+
+@supported=zh-CN, zh-TW, iw
+zh-Hant-TW >> zh-TW
+zh-Hant >> zh-TW
+zh-TW >> zh-TW
+zh-Hans-CN >> zh-CN
+zh-CN >> zh-CN
+zh >> zh-CN
+
+@distance=script
+zh-Hant-TW >> zh-TW
+zh-Hant >> zh-TW
+zh-TW >> zh-TW
+zh-Hans-CN >> zh-CN
+zh-CN >> zh-CN
+zh >> zh-CN
+
+** test: testenGB
+
+@supported=fr, en, en-GB, es-419, es-MX, es
+en-NZ >> en-GB
+es-ES >> es
+es-AR >> es-419
+es-MX >> es-MX
+
+@distance=script
+en-NZ >> en-GB
+es-ES >> es
+es-AR >> es-419
+es-MX >> es-MX
+
+** test: testFallbacks
+
+@supported=91, en, hi
+sa >> hi
+
+@distance=script
+sa >> hi
+
+** test: testBasics
+
+@supported=fr, en-GB, en
+en-GB >> en-GB
+en >> en
+fr >> fr
+ja >> fr # return first if no match
+
+@distance=script
+en-GB >> en-GB
+en >> en
+fr >> fr
+ja >> fr
+
+** test: testFallback
 
 # check that script fallbacks are handled right
 
-zh-CN, zh-TW, iw ;     zh-Hant ;       zh-TW
-zh-CN, zh-TW, iw ;     zh ;    zh-CN
-zh-CN, zh-TW, iw ;     zh-Hans-CN ;    zh-CN
-zh-CN, zh-TW, iw ;     zh-Hant-HK ;    zh-TW
-zh-CN, zh-TW, iw ;     he-IT ;         iw
+@supported=zh-CN, zh-TW, iw
+zh-Hant >> zh-TW
+zh >> zh-CN
+zh-Hans-CN >> zh-CN
+zh-Hant-HK >> zh-TW
+he-IT >> iw
+
+@distance=script
+zh-Hant >> zh-TW
+zh >> zh-CN
+zh-Hans-CN >> zh-CN
+zh-Hant-HK >> zh-TW
+he-IT >> iw
 
-##################################################
-# testSpecials
+** test: testSpecials
 
 # check that nearby languages are handled
 
-en, fil, ro, nn ;      tl ;    fil
-en, fil, ro, nn ;      mo ;    ro
-en, fil, ro, nn ;      nb ;    nn
+@supported=en, fil, ro, nn
+tl >> fil
+mo >> ro
+nb >> nn
 
 # make sure default works
 
-en, fil, ro, nn ;      ja ;    en
+ja >> en
+
+@distance=script
+tl >> fil
+mo >> ro
+nb >> nn
+ja >> en
 
-##################################################
-# testRegionalSpecials
+** test: testRegionalSpecials
 
 # verify that en-AU is closer to en-GB than to en (which is en-US)
 
-en, en-GB, es, es-419 ;        es-MX ;         es-419
-en, en-GB, es, es-419 ;        en-AU ;         en-GB
-en, en-GB, es, es-419 ;        es-ES ;         es
+@supported=en, en-GB, es, es-419
+es-MX >> es-419
+en-AU >> en-GB
+es-ES >> es
 
-##################################################
-# testHK
+@distance=script
+es-MX >> es-419
+en-AU >> en-GB
+es-ES >> es
+
+** test: testHK
 
 # HK and MO are closer to each other for Hant than to TW
 
-zh, zh-TW, zh-MO ;     zh-HK ;         zh-MO
-zh, zh-TW, zh-HK ;     zh-MO ;         zh-HK
+@supported=zh, zh-TW, zh-MO
+zh-HK >> zh-MO
+@supported=zh, zh-TW, zh-HK
+zh-MO >> zh-HK
+
+@distance=script
+@supported=zh, zh-TW, zh-MO
+zh-HK >> zh-MO
+@supported=zh, zh-TW, zh-HK
+zh-MO >> zh-HK
 
-##################################################
-# testMatch-exact
+** test: testMatch-matchOnMazimized
 
-# see localeDistance.txt
+@supported=zh, zh-Hant
+und-TW >> zh-Hant # und-TW should be closer to zh-Hant than to zh
 
-##################################################
-# testMatch-none
+@supported=en-Hant-TW, und-TW
+zh-Hant >> und-TW # zh-Hant should be closer to und-TW than to en-Hant-TW
+zh >> und-TW # zh should be closer to und-TW than to en-Hant-TW
 
-# see localeDistance.txt
+@distance=script
+@supported=zh, zh-Hant
+und-TW >> zh-Hant
+@supported=en-Hant-TW, und-TW
+zh-Hant >> und-TW
+zh >> und-TW
 
-##################################################
-# testMatch-matchOnMazimized
+** test: testMatchGrandfatheredCode
 
-zh, zh-Hant ;  und-TW ;        zh-Hant # und-TW should be closer to zh-Hant than to zh
-en-Hant-TW, und-TW ;   zh-Hant ;       und-TW  # zh-Hant should be closer to und-TW than to en-Hant-TW
-en-Hant-TW, und-TW ;   zh ;    und-TW  # zh should be closer to und-TW than to en-Hant-TW
+@supported=fr, i-klingon, en-Latn-US
+en-GB-oed >> en-Latn-US
 
-##################################################
-# testMatchGrandfatheredCode
+@distance=script
+en-GB-oed >> en-Latn-US
 
-fr, i-klingon, en-Latn-US ;    en-GB-oed ;     en-Latn-US
+** test: testGetBestMatchForList-exactMatch
+@supported=fr, en-GB, ja, es-ES, es-MX
+ja, de >> ja
 
-##################################################
-# testGetBestMatchForList-exactMatch
-fr, en-GB, ja, es-ES, es-MX ;  ja, de ;        ja
+@distance=script
+ja, de >> ja
 
-##################################################
-# testGetBestMatchForList-simpleVariantMatch
-fr, en-GB, ja, es-ES, es-MX ;  de, en-US ;     en-GB   # Intentionally avoiding a perfect-match or two candidates for variant matches.
+** test: testGetBestMatchForList-simpleVariantMatch
+@supported=fr, en-GB, ja, es-ES, es-MX
+de, en-US >> en-GB # Intentionally avoiding a perfect-match or two candidates for variant matches.
 
 # Fallback.
 
-fr, en-GB, ja, es-ES, es-MX ;  de, zh ;        fr
+de, zh >> fr
+
+@distance=script
+de, en-US >> en-GB
+de, zh >> fr
 
-##################################################
-# testGetBestMatchForList-matchOnMaximized
+** test: testGetBestMatchForList-matchOnMaximized
 # Check that if the preference is maximized already, it works as well.
 
-en, ja ;       ja-Jpan-JP, en-AU ;     ja      # Match for ja-Jpan-JP (maximized already)
+@supported=en, ja
+ja-Jpan-JP, en-AU >> ja # Match for ja-Jpan-JP (maximized already)
 
 # ja-JP matches ja on likely subtags, and it's listed first, thus it wins over the second preference en-GB.
 
-en, ja ;       ja-JP, en-US ;  ja      # Match for ja-Jpan-JP (maximized already)
+ja-JP, en-US >> ja # Match for ja-Jpan-JP (maximized already)
 
 # Check that if the preference is maximized already, it works as well.
 
-en, ja ;       ja-Jpan-JP, en-US ;     ja      # Match for ja-Jpan-JP (maximized already)
+ja-Jpan-JP, en-US >> ja # Match for ja-Jpan-JP (maximized already)
 
-##################################################
-# testGetBestMatchForList-noMatchOnMaximized
+@distance=script
+ja-Jpan-JP, en-AU >> ja
+ja-JP, en-US >> ja
+ja-Jpan-JP, en-US >> ja
+
+** test: testGetBestMatchForList-noMatchOnMaximized
 # Regression test for http://b/5714572 .
 # de maximizes to de-DE. Pick the exact match for the secondary language instead.
-en, de, fr, ja ;       de-CH, fr ;     de
+@supported=en, de, fr, ja
+de-CH, fr >> de
+
+@distance=script
+de-CH, fr >> de
 
-##################################################
-# testBestMatchForTraditionalChinese
+** test: testBestMatchForTraditionalChinese
 
 # Scenario: An application that only supports Simplified Chinese (and some other languages),
 # but does not support Traditional Chinese. zh-Hans-CN could be replaced with zh-CN, zh, or
@@ -197,28 +343,37 @@ en, de, fr, ja ;  de-CH, fr ;     de
 # The script distance (simplified vs. traditional Han) is considered small enough
 # to be an acceptable match. The regional difference is considered almost insignificant.
 
-fr, zh-Hans-CN, en-US ;        zh-TW ;         zh-Hans-CN
-fr, zh-Hans-CN, en-US ;        zh-Hant ;       zh-Hans-CN
+@supported=fr, zh-Hans-CN, en-US
+zh-TW >> zh-Hans-CN
+zh-Hant >> zh-Hans-CN
 
-# For geo-political reasons, you might want to avoid a zh-Hant -> zh-Hans match.
+# For geopolitical reasons, you might want to avoid a zh-Hant -> zh-Hans match.
 # In this case, if zh-TW, zh-HK or a tag starting with zh-Hant is requested, you can
 # change your call to getBestMatch to include a 2nd language preference.
 # "en" is a better match since its distance to "en-US" is closer than the distance
 # from "zh-TW" to "zh-CN" (script distance).
 
-fr, zh-Hans-CN, en-US ;        zh-TW, en ;     en-US
-fr, zh-Hans-CN, en-US ;        zh-Hant-CN, en, en ;    en-US
-fr, zh-Hans-CN, en-US ;        zh-Hans, en ;   zh-Hans-CN
+zh-TW, en >> en-US
+zh-Hant-CN, en >> en-US
+zh-Hans, en >> zh-Hans-CN
+
+@distance=script
+zh-TW >> zh-Hans-CN
+zh-Hant >> zh-Hans-CN
+zh-TW, en >> en-US
+zh-Hant-CN, en >> en-US
+zh-Hans, en >> zh-Hans-CN
 
-##################################################
-# testUndefined
+** test: testUndefined
 # When the undefined language doesn't match anything in the list,
 # getBestMatch returns the default, as usual.
 
-it, fr ;       und ;   it
+@supported=it, fr
+und >> it
 
 # When it *does* occur in the list, bestMatch returns it, as expected.
-it, und ;      und ;   und
+@supported=it, und
+und >> und
 
 # The unusual part: max("und") = "en-Latn-US", and since matching is based on maximized
 # tags, the undefined language would normally match English. But that would produce the
@@ -229,159 +384,1570 @@ it, und ;      und ;   und
 # so that max("und")="und". That produces the following, more desirable
 # results:
 
-it, en ;       und ;   it
-it, und ;      en ;    it
-
-##################################################
-# testGetBestMatch-regionDistance
-
-es-AR, es ;    es-MX ;         es-AR
-fr, en, en-GB ;        en-CA ;         en-GB
-de-AT, de-DE, de-CH ;  de ;    de-DE
-
-##################################################
-# testAsymmetry
-
-mul, nl ;      af ;    nl      # af => nl
-mul, af ;      nl ;    mul     # but nl !=> af
-
-##################################################
-# testGetBestMatchForList-matchOnMaximized2
+@supported=it, en
+und >> it
+@supported=it, und
+en >> it
+
+@distance=script
+@supported=it, fr
+und >> it
+@supported=it, und
+und >> und
+@supported=it, en
+und >> it
+@supported=it, und
+en >> it
+
+** test: testGetBestMatch-regionDistance
+
+@supported=es-AR, es
+es-MX >> es-AR
+@supported=fr, en, en-GB
+en-CA >> en-GB
+@supported=de-AT, de-DE, de-CH
+de >> de-DE
+
+@distance=script
+@supported=es-AR, es
+es-MX >> es-AR
+@supported=fr, en, en-GB
+en-CA >> en-GB
+@supported=de-AT, de-DE, de-CH
+de >> de-DE
+
+** test: testAsymmetry
+
+@supported=mul, nl
+af >> nl # af => nl
+@supported=mul, af
+nl >> mul # but nl !=> af
+
+@distance=script
+@supported=mul, nl
+af >> nl
+@supported=mul, af
+nl >> mul
+
+** test: testGetBestMatchForList-matchOnMaximized2
 
 # ja-JP matches ja on likely subtags, and it's listed first, thus it wins over the second preference en-GB.
 
-fr, en-GB, ja, es-ES, es-MX ;  ja-JP, en-GB ;  ja      # Match for ja-JP, with likely region subtag
+@supported=fr, en-GB, ja, es-ES, es-MX
+ja-JP, en-GB >> ja # Match for ja-JP, with likely region subtag
 
 # Check that if the preference is maximized already, it works as well.
 
-fr, en-GB, ja, es-ES, es-MX ;  ja-Jpan-JP, en-GB ;     ja      # Match for ja-Jpan-JP (maximized already)
+ja-Jpan-JP, en-GB >> ja # Match for ja-Jpan-JP (maximized already)
+
+@distance=script
+ja-JP, en-GB >> ja
+ja-Jpan-JP, en-GB >> ja
+
+** test: testGetBestMatchForList-closeEnoughMatchOnMaximized
 
-##################################################
-# testGetBestMatchForList-closeEnoughMatchOnMaximized
+@supported=en-GB, en, de, fr, ja
+de-CH, fr >> de
+en-US, ar, nl, de, ja >> en
 
-en-GB, en, de, fr, ja ;        de-CH, fr ;     de
-en-GB, en, de, fr, ja ;        en-US, ar, nl, de, ja ;         en
+@distance=script
+de-CH, fr >> de
+en-US, ar, nl, de, ja >> en
 
-##################################################
-# testGetBestMatchForPortuguese
+** test: testGetBestMatchForPortuguese
 
 # pt might be supported and not pt-PT
 
-# European user who prefers Spanish over Brazillian Portuguese as a fallback.
+# European user who prefers Spanish over Brazilian Portuguese as a fallback.
 
-pt-PT, pt-BR, es, es-419 ;     pt-PT, es, pt ;         pt-PT
-pt-PT, pt, es, es-419 ;        pt-PT, es, pt ;         pt-PT   # pt implicit
+@supported=pt-PT, pt-BR, es, es-419
+pt-PT, es, pt >> pt-PT
+@supported=pt-PT, pt, es, es-419
+pt-PT, es, pt >> pt-PT # pt implicit
 
-# Brazillian user who prefers South American Spanish over European Portuguese as a fallback.
+# Brazilian user who prefers South American Spanish over European Portuguese as a fallback.
 # The asymmetry between this case and above is because it's "pt-PT" that's missing between the
 # matchers as "pt-BR" is a much more common language.
 
-pt-PT, pt-BR, es, es-419 ;     pt, es-419, pt-PT ;     pt-BR
-pt-PT, pt-BR, es, es-419 ;     pt-PT, es, pt ;         pt-PT
-pt-PT, pt, es, es-419 ;        pt-PT, es, pt ;         pt-PT
-pt-PT, pt, es, es-419 ;        pt, es-419, pt-PT ;     pt
+@supported=pt-PT, pt-BR, es, es-419
+pt, es-419, pt-PT >> pt-BR
+pt-PT, es, pt >> pt-PT
+@supported=pt-PT, pt, es, es-419
+pt-PT, es, pt >> pt-PT
+pt, es-419, pt-PT >> pt
 
-pt-BR, es, es-419 ;    pt, es-419, pt-PT ;     pt-BR
+@supported=pt-BR, es, es-419
+pt, es-419, pt-PT >> pt-BR
 
 # Code that adds the user's country can get "pt-US" for a user's language.
 # That should fall back to "pt-BR".
 
-pt-PT, pt-BR, es, es-419 ;     pt-US, pt-PT ;  pt-BR
-pt-PT, pt, es, es-419 ;        pt-US, pt-PT, pt ;      pt      # pt-BR implicit
+@supported=pt-PT, pt-BR, es, es-419
+pt-US, pt-PT >> pt-BR
+@supported=pt-PT, pt, es, es-419
+pt-US, pt-PT, pt >> pt # pt-BR implicit
+
+@distance=script
+@supported=pt-PT, pt-BR, es, es-419
+pt-PT, es, pt >> pt-PT
+@supported=pt-PT, pt, es, es-419
+pt-PT, es, pt >> pt-PT
+
+@supported=pt-PT, pt-BR, es, es-419
+pt, es-419, pt-PT >> pt-BR
+pt-PT, es, pt >> pt-PT
+@supported=pt-PT, pt, es, es-419
+pt-PT, es, pt >> pt-PT
+pt, es-419, pt-PT >> pt
+
+@supported=pt-BR, es, es-419
+pt, es-419, pt-PT >> pt-BR
+
+@supported=pt-PT, pt-BR, es, es-419
+pt-US, pt-PT >> pt-BR
+@supported=pt-PT, pt, es, es-419
+pt-US, pt-PT, pt >> pt
+
+** test: testVariantWithScriptMatch 1 and 2
+
+@supported=fr, en, sv
+en-GB >> en
+@supported=en, sv
+en-GB, sv >> en
 
-##################################################
-# testVariantWithScriptMatch 1 and 2
+@distance=script
+@supported=fr, en, sv
+en-GB >> en
+@supported=en, sv
+en-GB, sv >> en
 
-fr, en, sv ;   en-GB ;         en
-fr, en, sv ;   en-GB ;         en
-en, sv ;       en-GB, sv ;     en
+** test: testLongLists
 
-##################################################
-# testLongLists
+@supported=en, sv
+sv >> sv
 
-en, sv ;       sv ;    sv
-af, am, ar, az, be, bg, bn, bs, ca, cs, cy, cy, da, de, el, en, en-GB, es, es-419, et, eu, fa, fi, fil, fr, ga, gl, gu, hi, hr, hu, hy, id, is, it, iw, ja, ka, kk, km, kn, ko, ky, lo, lt, lv, mk, ml, mn, mr, ms, my, ne, nl, no, pa, pl, pt, pt-PT, ro, ru, si, sk, sl, sq, sr, sr-Latn, sv, sw, ta, te, th, tr, uk, ur, uz, vi, zh-CN, zh-TW, zu ;         sv ;    sv
-af, af-NA, af-ZA, agq, agq-CM, ak, ak-GH, am, am-ET, ar, ar-001, ar-AE, ar-BH, ar-DJ, ar-DZ, ar-EG, ar-EH, ar-ER, ar-IL, ar-IQ, ar-JO, ar-KM, ar-KW, ar-LB, ar-LY, ar-MA, ar-MR, ar-OM, ar-PS, ar-QA, ar-SA, ar-SD, ar-SO, ar-SS, ar-SY, ar-TD, ar-TN, ar-YE, as, as-IN, asa, asa-TZ, ast, ast-ES, az, az-Cyrl, az-Cyrl-AZ, az-Latn, az-Latn-AZ, bas, bas-CM, be, be-BY, bem, bem-ZM, bez, bez-TZ, bg, bg-BG, bm, bm-ML, bn, bn-BD, bn-IN, bo, bo-CN, bo-IN, br, br-FR, brx, brx-IN, bs, bs-Cyrl, bs-Cyrl-BA, bs-Latn, bs-Latn-BA, ca, ca-AD, ca-ES, ca-ES-VALENCIA, ca-FR, ca-IT, ce, ce-RU, cgg, cgg-UG, chr, chr-US, ckb, ckb-IQ, ckb-IR, cs, cs-CZ, cu, cu-RU, cy, cy-GB, da, da-DK, da-GL, dav, dav-KE, de, de-AT, de-BE, de-CH, de-DE, de-LI, de-LU, dje, dje-NE, dsb, dsb-DE, dua, dua-CM, dyo, dyo-SN, dz, dz-BT, ebu, ebu-KE, ee, ee-GH, ee-TG, el, el-CY, el-GR, en, en-001, en-150, en-AG, en-AI, en-AS, en-AT, en-AU, en-BB, en-BE, en-BI, en-BM, en-BS, en-BW, en-BZ, en-CA, en-CC, en-CH, en-CK, en-CM, en-CX, en-CY, en-DE, en-DG, en-DK, en-DM, en-ER, en-FI, en-FJ, en-FK, en-FM, en-GB, en-GD, en-GG, en-GH, en-GI, en-GM, en-GU, en-GY, en-HK, en-IE, en-IL, en-IM, en-IN, en-IO, en-JE, en-JM, en-KE, en-KI, en-KN, en-KY, en-LC, en-LR, en-LS, en-MG, en-MH, en-MO, en-MP, en-MS, en-MT, en-MU, en-MW, en-MY, en-NA, en-NF, en-NG, en-NL, en-NR, en-NU, en-NZ, en-PG, en-PH, en-PK, en-PN, en-PR, en-PW, en-RW, en-SB, en-SC, en-SD, en-SE, en-SG, en-SH, en-SI, en-SL, en-SS, en-SX, en-SZ, en-TC, en-TK, en-TO, en-TT, en-TV, en-TZ, en-UG, en-UM, en-US, en-US-POSIX, en-VC, en-VG, en-VI, en-VU, en-WS, en-ZA, en-ZM, en-ZW, eo, eo-001, es, es-419, es-AR, es-BO, es-CL, es-CO, es-CR, es-CU, es-DO, es-EA, es-EC, es-ES, es-GQ, es-GT, es-HN, es-IC, es-MX, es-NI, es-PA, es-PE, es-PH, es-PR, es-PY, es-SV, es-US, es-UY, es-VE, et, et-EE, eu, eu-ES, ewo, ewo-CM, fa, fa-AF, fa-IR, ff, ff-CM, ff-GN, ff-MR, ff-SN, fi, fi-FI, fil, fil-PH, fo, fo-DK, fo-FO, fr, fr-BE, fr-BF, fr-BI, fr-BJ, fr-BL, fr-CA, fr-CD, fr-CF, fr-CG, fr-CH, fr-CI, fr-CM, fr-DJ, fr-DZ, fr-FR, fr-GA, fr-GF, fr-GN, fr-GP, fr-GQ, fr-HT, fr-KM, fr-LU, fr-MA, fr-MC, fr-MF, fr-MG, fr-ML, fr-MQ, fr-MR, fr-MU, fr-NC, fr-NE, fr-PF, fr-PM, fr-RE, fr-RW, fr-SC, fr-SN, fr-SY, fr-TD, fr-TG, fr-TN, fr-VU, fr-WF, fr-YT, fur, fur-IT, fy, fy-NL, ga, ga-IE, gd, gd-GB, gl, gl-ES, gsw, gsw-CH, gsw-FR, gsw-LI, gu, gu-IN, guz, guz-KE, gv, gv-IM, ha, ha-GH, ha-NE, ha-NG, haw, haw-US, he, he-IL, hi, hi-IN, hr, hr-BA, hr-HR, hsb, hsb-DE, hu, hu-HU, hy, hy-AM, id, id-ID, ig, ig-NG, ii, ii-CN, is, is-IS, it, it-CH, it-IT, it-SM, ja, ja-JP, jgo, jgo-CM, jmc, jmc-TZ, ka, ka-GE, kab, kab-DZ, kam, kam-KE, kde, kde-TZ, kea, kea-CV, khq, khq-ML, ki, ki-KE, kk, kk-KZ, kkj, kkj-CM, kl, kl-GL, kln, kln-KE, km, km-KH, kn, kn-IN, ko, ko-KP, ko-KR, kok, kok-IN, ks, ks-IN, ksb, ksb-TZ, ksf, ksf-CM, ksh, ksh-DE, kw, kw-GB, ky, ky-KG, lag, lag-TZ, lb, lb-LU, lg, lg-UG, lkt, lkt-US, ln, ln-AO, ln-CD, ln-CF, ln-CG, lo, lo-LA, lrc, lrc-IQ, lrc-IR, lt, lt-LT, lu, lu-CD, luo, luo-KE, luy, luy-KE, lv, lv-LV, mas, mas-KE, mas-TZ, mer, mer-KE, mfe, mfe-MU, mg, mg-MG, mgh, mgh-MZ, mgo, mgo-CM, mk, mk-MK, ml, ml-IN, mn, mn-MN, mr, mr-IN, ms, ms-BN, ms-MY, ms-SG, mt, mt-MT, mua, mua-CM, my, my-MM, mzn, mzn-IR, naq, naq-NA, nb, nb-NO, nb-SJ, nd, nd-ZW, ne, ne-IN, ne-NP, nl, nl-AW, nl-BE, nl-BQ, nl-CW, nl-NL, nl-SR, nl-SX, nmg, nmg-CM, nn, nn-NO, nnh, nnh-CM, nus, nus-SS, nyn, nyn-UG, om, om-ET, om-KE, or, or-IN, os, os-GE, os-RU, pa, pa-Arab, pa-Arab-PK, pa-Guru, pa-Guru-IN, pl, pl-PL, prg, prg-001, ps, ps-AF, pt, pt-AO, pt-BR, pt-CV, pt-GW, pt-MO, pt-MZ, pt-PT, pt-ST, pt-TL, qu, qu-BO, qu-EC, qu-PE, rm, rm-CH, rn, rn-BI, ro, ro-MD, ro-RO, rof, rof-TZ, root, ru, ru-BY, ru-KG, ru-KZ, ru-MD, ru-RU, ru-UA, rw, rw-RW, rwk, rwk-TZ, sah, sah-RU, saq, saq-KE, sbp, sbp-TZ, se, se-FI, se-NO, se-SE, seh, seh-MZ, ses, ses-ML, sg, sg-CF, shi, shi-Latn, shi-Latn-MA, shi-Tfng, shi-Tfng-MA, si, si-LK, sk, sk-SK, sl, sl-SI, smn, smn-FI, sn, sn-ZW, so, so-DJ, so-ET, so-KE, so-SO, sq, sq-AL, sq-MK, sq-XK, sr, sr-Cyrl, sr-Cyrl-BA, sr-Cyrl-ME, sr-Cyrl-RS, sr-Cyrl-XK, sr-Latn, sr-Latn-BA, sr-Latn-ME, sr-Latn-RS, sr-Latn-XK, sv, sv-AX, sv-FI, sv-SE, sw, sw-CD, sw-KE, sw-TZ, sw-UG, ta, ta-IN, ta-LK, ta-MY, ta-SG, te, te-IN, teo, teo-KE, teo-UG, th, th-TH, ti, ti-ER, ti-ET, tk, tk-TM, to, to-TO, tr, tr-CY, tr-TR, twq, twq-NE, tzm, tzm-MA, ug, ug-CN, uk, uk-UA, ur, ur-IN, ur-PK, uz, uz-Arab, uz-Arab-AF, uz-Cyrl, uz-Cyrl-UZ, uz-Latn, uz-Latn-UZ, vai, vai-Latn, vai-Latn-LR, vai-Vaii, vai-Vaii-LR, vi, vi-VN, vo, vo-001, vun, vun-TZ, wae, wae-CH, xog, xog-UG, yav, yav-CM, yi, yi-001, yo, yo-BJ, yo-NG, zgh, zgh-MA, zh, zh-Hans, zh-Hans-CN, zh-Hans-HK, zh-Hans-MO, zh-Hans-SG, zh-Hant, zh-Hant-HK, zh-Hant-MO, zh-Hant-TW, zu, zu-ZA ;  sv ;    sv
+@supported=af, am, ar, az, be, bg, bn, bs, ca, cs, cy, da, de, el, en, en-GB, es, es-419, et, eu, fa, fi, fil, fr, ga, gl, gu, hi, hr, hu, hy, id, is, it, iw, ja, ka, kk, km, kn, ko, ky, lo, lt, lv, mk, ml, mn, mr, ms, my, ne, nl, no, pa, pl, pt, pt-PT, ro, ru, si, sk, sl, sq, sr, sr-Latn, sv, sw, ta, te, th, tr, uk, ur, uz, vi, zh-CN, zh-TW, zu
+sv >> sv
 
-##################################################
-# test8288
+@supported=af, af-NA, af-ZA, agq, agq-CM, ak, ak-GH, am, am-ET, ar, ar-001, ar-AE, ar-BH, ar-DJ, ar-DZ, ar-EG, ar-EH, ar-ER, ar-IL, ar-IQ, ar-JO, ar-KM, ar-KW, ar-LB, ar-LY, ar-MA, ar-MR, ar-OM, ar-PS, ar-QA, ar-SA, ar-SD, ar-SO, ar-SS, ar-SY, ar-TD, ar-TN, ar-YE, as, as-IN, asa, asa-TZ, ast, ast-ES, az, az-Cyrl, az-Cyrl-AZ, az-Latn, az-Latn-AZ, bas, bas-CM, be, be-BY, bem, bem-ZM, bez, bez-TZ, bg, bg-BG, bm, bm-ML, bn, bn-BD, bn-IN, bo, bo-CN, bo-IN, br, br-FR, brx, brx-IN, bs, bs-Cyrl, bs-Cyrl-BA, bs-Latn, bs-Latn-BA, ca, ca-AD, ca-ES, ca-ES-VALENCIA, ca-FR, ca-IT, ce, ce-RU, cgg, cgg-UG, chr, chr-US, ckb, ckb-IQ, ckb-IR, cs, cs-CZ, cu, cu-RU, cy, cy-GB, da, da-DK, da-GL, dav, dav-KE, de, de-AT, de-BE, de-CH, de-DE, de-LI, de-LU, dje, dje-NE, dsb, dsb-DE, dua, dua-CM, dyo, dyo-SN, dz, dz-BT, ebu, ebu-KE, ee, ee-GH, ee-TG, el, el-CY, el-GR, en, en-001, en-150, en-AG, en-AI, en-AS, en-AT, en-AU, en-BB, en-BE, en-BI, en-BM, en-BS, en-BW, en-BZ, en-CA, en-CC, en-CH, en-CK, en-CM, en-CX, en-CY, en-DE, en-DG, en-DK, en-DM, en-ER, en-FI, en-FJ, en-FK, en-FM, en-GB, en-GD, en-GG, en-GH, en-GI, en-GM, en-GU, en-GY, en-HK, en-IE, en-IL, en-IM, en-IN, en-IO, en-JE, en-JM, en-KE, en-KI, en-KN, en-KY, en-LC, en-LR, en-LS, en-MG, en-MH, en-MO, en-MP, en-MS, en-MT, en-MU, en-MW, en-MY, en-NA, en-NF, en-NG, en-NL, en-NR, en-NU, en-NZ, en-PG, en-PH, en-PK, en-PN, en-PR, en-PW, en-RW, en-SB, en-SC, en-SD, en-SE, en-SG, en-SH, en-SI, en-SL, en-SS, en-SX, en-SZ, en-TC, en-TK, en-TO, en-TT, en-TV, en-TZ, en-UG, en-UM, en-US, en-US-POSIX, en-VC, en-VG, en-VI, en-VU, en-WS, en-ZA, en-ZM, en-ZW, eo, eo-001, es, es-419, es-AR, es-BO, es-CL, es-CO, es-CR, es-CU, es-DO, es-EA, es-EC, es-ES, es-GQ, es-GT, es-HN, es-IC, es-MX, es-NI, es-PA, es-PE, es-PH, es-PR, es-PY, es-SV, es-US, es-UY, es-VE, et, et-EE, eu, eu-ES, ewo, ewo-CM, fa, fa-AF, fa-IR, ff, ff-CM, ff-GN, ff-MR, ff-SN, fi, fi-FI, fil, fil-PH, fo, fo-DK, fo-FO, fr, fr-BE, fr-BF, fr-BI, fr-BJ, fr-BL, fr-CA, fr-CD, fr-CF, fr-CG, fr-CH, fr-CI, fr-CM, fr-DJ, fr-DZ, fr-FR, fr-GA, fr-GF, fr-GN, fr-GP, fr-GQ, fr-HT, fr-KM, fr-LU, fr-MA, fr-MC, fr-MF, fr-MG, fr-ML, fr-MQ, fr-MR, fr-MU, fr-NC, fr-NE, fr-PF, fr-PM, fr-RE, fr-RW, fr-SC, fr-SN, fr-SY, fr-TD, fr-TG, fr-TN, fr-VU, fr-WF, fr-YT, fur, fur-IT, fy, fy-NL, ga, ga-IE, gd, gd-GB, gl, gl-ES, gsw, gsw-CH, gsw-FR, gsw-LI, gu, gu-IN, guz, guz-KE, gv, gv-IM, ha, ha-GH, ha-NE, ha-NG, haw, haw-US, he, he-IL, hi, hi-IN, hr, hr-BA, hr-HR, hsb, hsb-DE, hu, hu-HU, hy, hy-AM, id, id-ID, ig, ig-NG, ii, ii-CN, is, is-IS, it, it-CH, it-IT, it-SM, ja, ja-JP, jgo, jgo-CM, jmc, jmc-TZ, ka, ka-GE, kab, kab-DZ, kam, kam-KE, kde, kde-TZ, kea, kea-CV, khq, khq-ML, ki, ki-KE, kk, kk-KZ, kkj, kkj-CM, kl, kl-GL, kln, kln-KE, km, km-KH, kn, kn-IN, ko, ko-KP, ko-KR, kok, kok-IN, ks, ks-IN, ksb, ksb-TZ, ksf, ksf-CM, ksh, ksh-DE, kw, kw-GB, ky, ky-KG, lag, lag-TZ, lb, lb-LU, lg, lg-UG, lkt, lkt-US, ln, ln-AO, ln-CD, ln-CF, ln-CG, lo, lo-LA, lrc, lrc-IQ, lrc-IR, lt, lt-LT, lu, lu-CD, luo, luo-KE, luy, luy-KE, lv, lv-LV, mas, mas-KE, mas-TZ, mer, mer-KE, mfe, mfe-MU, mg, mg-MG, mgh, mgh-MZ, mgo, mgo-CM, mk, mk-MK, ml, ml-IN, mn, mn-MN, mr, mr-IN, ms, ms-BN, ms-MY, ms-SG, mt, mt-MT, mua, mua-CM, my, my-MM, mzn, mzn-IR, naq, naq-NA, nb, nb-NO, nb-SJ, nd, nd-ZW, ne, ne-IN, ne-NP, nl, nl-AW, nl-BE, nl-BQ, nl-CW, nl-NL, nl-SR, nl-SX, nmg, nmg-CM, nn, nn-NO, nnh, nnh-CM, nus, nus-SS, nyn, nyn-UG, om, om-ET, om-KE, or, or-IN, os, os-GE, os-RU, pa, pa-Arab, pa-Arab-PK, pa-Guru, pa-Guru-IN, pl, pl-PL, prg, prg-001, ps, ps-AF, pt, pt-AO, pt-BR, pt-CV, pt-GW, pt-MO, pt-MZ, pt-PT, pt-ST, pt-TL, qu, qu-BO, qu-EC, qu-PE, rm, rm-CH, rn, rn-BI, ro, ro-MD, ro-RO, rof, rof-TZ, root, ru, ru-BY, ru-KG, ru-KZ, ru-MD, ru-RU, ru-UA, rw, rw-RW, rwk, rwk-TZ, sah, sah-RU, saq, saq-KE, sbp, sbp-TZ, se, se-FI, se-NO, se-SE, seh, seh-MZ, ses, ses-ML, sg, sg-CF, shi, shi-Latn, shi-Latn-MA, shi-Tfng, shi-Tfng-MA, si, si-LK, sk, sk-SK, sl, sl-SI, smn, smn-FI, sn, sn-ZW, so, so-DJ, so-ET, so-KE, so-SO, sq, sq-AL, sq-MK, sq-XK, sr, sr-Cyrl, sr-Cyrl-BA, sr-Cyrl-ME, sr-Cyrl-RS, sr-Cyrl-XK, sr-Latn, sr-Latn-BA, sr-Latn-ME, sr-Latn-RS, sr-Latn-XK, sv, sv-AX, sv-FI, sv-SE, sw, sw-CD, sw-KE, sw-TZ, sw-UG, ta, ta-IN, ta-LK, ta-MY, ta-SG, te, te-IN, teo, teo-KE, teo-UG, th, th-TH, ti, ti-ER, ti-ET, tk, tk-TM, to, to-TO, tr, tr-CY, tr-TR, twq, twq-NE, tzm, tzm-MA, ug, ug-CN, uk, uk-UA, ur, ur-IN, ur-PK, uz, uz-Arab, uz-Arab-AF, uz-Cyrl, uz-Cyrl-UZ, uz-Latn, uz-Latn-UZ, vai, vai-Latn, vai-Latn-LR, vai-Vaii, vai-Vaii-LR, vi, vi-VN, vo, vo-001, vun, vun-TZ, wae, wae-CH, xog, xog-UG, yav, yav-CM, yi, yi-001, yo, yo-BJ, yo-NG, zgh, zgh-MA, zh, zh-Hans, zh-Hans-CN, zh-Hans-HK, zh-Hans-MO, zh-Hans-SG, zh-Hant, zh-Hant-HK, zh-Hant-MO, zh-Hant-TW, zu, zu-ZA
+sv >> sv
 
-it, en ;       und ;   it
-it, en ;       und, en ;       en
+@distance=script
+@supported=en, sv
+sv >> sv
+
+@supported=af, am, ar, az, be, bg, bn, bs, ca, cs, cy, da, de, el, en, en-GB, es, es-419, et, eu, fa, fi, fil, fr, ga, gl, gu, hi, hr, hu, hy, id, is, it, iw, ja, ka, kk, km, kn, ko, ky, lo, lt, lv, mk, ml, mn, mr, ms, my, ne, nl, no, pa, pl, pt, pt-PT, ro, ru, si, sk, sl, sq, sr, sr-Latn, sv, sw, ta, te, th, tr, uk, ur, uz, vi, zh-CN, zh-TW, zu
+sv >> sv
+
+@supported=af, af-NA, af-ZA, agq, agq-CM, ak, ak-GH, am, am-ET, ar, ar-001, ar-AE, ar-BH, ar-DJ, ar-DZ, ar-EG, ar-EH, ar-ER, ar-IL, ar-IQ, ar-JO, ar-KM, ar-KW, ar-LB, ar-LY, ar-MA, ar-MR, ar-OM, ar-PS, ar-QA, ar-SA, ar-SD, ar-SO, ar-SS, ar-SY, ar-TD, ar-TN, ar-YE, as, as-IN, asa, asa-TZ, ast, ast-ES, az, az-Cyrl, az-Cyrl-AZ, az-Latn, az-Latn-AZ, bas, bas-CM, be, be-BY, bem, bem-ZM, bez, bez-TZ, bg, bg-BG, bm, bm-ML, bn, bn-BD, bn-IN, bo, bo-CN, bo-IN, br, br-FR, brx, brx-IN, bs, bs-Cyrl, bs-Cyrl-BA, bs-Latn, bs-Latn-BA, ca, ca-AD, ca-ES, ca-ES-VALENCIA, ca-FR, ca-IT, ce, ce-RU, cgg, cgg-UG, chr, chr-US, ckb, ckb-IQ, ckb-IR, cs, cs-CZ, cu, cu-RU, cy, cy-GB, da, da-DK, da-GL, dav, dav-KE, de, de-AT, de-BE, de-CH, de-DE, de-LI, de-LU, dje, dje-NE, dsb, dsb-DE, dua, dua-CM, dyo, dyo-SN, dz, dz-BT, ebu, ebu-KE, ee, ee-GH, ee-TG, el, el-CY, el-GR, en, en-001, en-150, en-AG, en-AI, en-AS, en-AT, en-AU, en-BB, en-BE, en-BI, en-BM, en-BS, en-BW, en-BZ, en-CA, en-CC, en-CH, en-CK, en-CM, en-CX, en-CY, en-DE, en-DG, en-DK, en-DM, en-ER, en-FI, en-FJ, en-FK, en-FM, en-GB, en-GD, en-GG, en-GH, en-GI, en-GM, en-GU, en-GY, en-HK, en-IE, en-IL, en-IM, en-IN, en-IO, en-JE, en-JM, en-KE, en-KI, en-KN, en-KY, en-LC, en-LR, en-LS, en-MG, en-MH, en-MO, en-MP, en-MS, en-MT, en-MU, en-MW, en-MY, en-NA, en-NF, en-NG, en-NL, en-NR, en-NU, en-NZ, en-PG, en-PH, en-PK, en-PN, en-PR, en-PW, en-RW, en-SB, en-SC, en-SD, en-SE, en-SG, en-SH, en-SI, en-SL, en-SS, en-SX, en-SZ, en-TC, en-TK, en-TO, en-TT, en-TV, en-TZ, en-UG, en-UM, en-US, en-US-POSIX, en-VC, en-VG, en-VI, en-VU, en-WS, en-ZA, en-ZM, en-ZW, eo, eo-001, es, es-419, es-AR, es-BO, es-CL, es-CO, es-CR, es-CU, es-DO, es-EA, es-EC, es-ES, es-GQ, es-GT, es-HN, es-IC, es-MX, es-NI, es-PA, es-PE, es-PH, es-PR, es-PY, es-SV, es-US, es-UY, es-VE, et, et-EE, eu, eu-ES, ewo, ewo-CM, fa, fa-AF, fa-IR, ff, ff-CM, ff-GN, ff-MR, ff-SN, fi, fi-FI, fil, fil-PH, fo, fo-DK, fo-FO, fr, fr-BE, fr-BF, fr-BI, fr-BJ, fr-BL, fr-CA, fr-CD, fr-CF, fr-CG, fr-CH, fr-CI, fr-CM, fr-DJ, fr-DZ, fr-FR, fr-GA, fr-GF, fr-GN, fr-GP, fr-GQ, fr-HT, fr-KM, fr-LU, fr-MA, fr-MC, fr-MF, fr-MG, fr-ML, fr-MQ, fr-MR, fr-MU, fr-NC, fr-NE, fr-PF, fr-PM, fr-RE, fr-RW, fr-SC, fr-SN, fr-SY, fr-TD, fr-TG, fr-TN, fr-VU, fr-WF, fr-YT, fur, fur-IT, fy, fy-NL, ga, ga-IE, gd, gd-GB, gl, gl-ES, gsw, gsw-CH, gsw-FR, gsw-LI, gu, gu-IN, guz, guz-KE, gv, gv-IM, ha, ha-GH, ha-NE, ha-NG, haw, haw-US, he, he-IL, hi, hi-IN, hr, hr-BA, hr-HR, hsb, hsb-DE, hu, hu-HU, hy, hy-AM, id, id-ID, ig, ig-NG, ii, ii-CN, is, is-IS, it, it-CH, it-IT, it-SM, ja, ja-JP, jgo, jgo-CM, jmc, jmc-TZ, ka, ka-GE, kab, kab-DZ, kam, kam-KE, kde, kde-TZ, kea, kea-CV, khq, khq-ML, ki, ki-KE, kk, kk-KZ, kkj, kkj-CM, kl, kl-GL, kln, kln-KE, km, km-KH, kn, kn-IN, ko, ko-KP, ko-KR, kok, kok-IN, ks, ks-IN, ksb, ksb-TZ, ksf, ksf-CM, ksh, ksh-DE, kw, kw-GB, ky, ky-KG, lag, lag-TZ, lb, lb-LU, lg, lg-UG, lkt, lkt-US, ln, ln-AO, ln-CD, ln-CF, ln-CG, lo, lo-LA, lrc, lrc-IQ, lrc-IR, lt, lt-LT, lu, lu-CD, luo, luo-KE, luy, luy-KE, lv, lv-LV, mas, mas-KE, mas-TZ, mer, mer-KE, mfe, mfe-MU, mg, mg-MG, mgh, mgh-MZ, mgo, mgo-CM, mk, mk-MK, ml, ml-IN, mn, mn-MN, mr, mr-IN, ms, ms-BN, ms-MY, ms-SG, mt, mt-MT, mua, mua-CM, my, my-MM, mzn, mzn-IR, naq, naq-NA, nb, nb-NO, nb-SJ, nd, nd-ZW, ne, ne-IN, ne-NP, nl, nl-AW, nl-BE, nl-BQ, nl-CW, nl-NL, nl-SR, nl-SX, nmg, nmg-CM, nn, nn-NO, nnh, nnh-CM, nus, nus-SS, nyn, nyn-UG, om, om-ET, om-KE, or, or-IN, os, os-GE, os-RU, pa, pa-Arab, pa-Arab-PK, pa-Guru, pa-Guru-IN, pl, pl-PL, prg, prg-001, ps, ps-AF, pt, pt-AO, pt-BR, pt-CV, pt-GW, pt-MO, pt-MZ, pt-PT, pt-ST, pt-TL, qu, qu-BO, qu-EC, qu-PE, rm, rm-CH, rn, rn-BI, ro, ro-MD, ro-RO, rof, rof-TZ, root, ru, ru-BY, ru-KG, ru-KZ, ru-MD, ru-RU, ru-UA, rw, rw-RW, rwk, rwk-TZ, sah, sah-RU, saq, saq-KE, sbp, sbp-TZ, se, se-FI, se-NO, se-SE, seh, seh-MZ, ses, ses-ML, sg, sg-CF, shi, shi-Latn, shi-Latn-MA, shi-Tfng, shi-Tfng-MA, si, si-LK, sk, sk-SK, sl, sl-SI, smn, smn-FI, sn, sn-ZW, so, so-DJ, so-ET, so-KE, so-SO, sq, sq-AL, sq-MK, sq-XK, sr, sr-Cyrl, sr-Cyrl-BA, sr-Cyrl-ME, sr-Cyrl-RS, sr-Cyrl-XK, sr-Latn, sr-Latn-BA, sr-Latn-ME, sr-Latn-RS, sr-Latn-XK, sv, sv-AX, sv-FI, sv-SE, sw, sw-CD, sw-KE, sw-TZ, sw-UG, ta, ta-IN, ta-LK, ta-MY, ta-SG, te, te-IN, teo, teo-KE, teo-UG, th, th-TH, ti, ti-ER, ti-ET, tk, tk-TM, to, to-TO, tr, tr-CY, tr-TR, twq, twq-NE, tzm, tzm-MA, ug, ug-CN, uk, uk-UA, ur, ur-IN, ur-PK, uz, uz-Arab, uz-Arab-AF, uz-Cyrl, uz-Cyrl-UZ, uz-Latn, uz-Latn-UZ, vai, vai-Latn, vai-Latn-LR, vai-Vaii, vai-Vaii-LR, vi, vi-VN, vo, vo-001, vun, vun-TZ, wae, wae-CH, xog, xog-UG, yav, yav-CM, yi, yi-001, yo, yo-BJ, yo-NG, zgh, zgh-MA, zh, zh-Hans, zh-Hans-CN, zh-Hans-HK, zh-Hans-MO, zh-Hans-SG, zh-Hant, zh-Hant-HK, zh-Hant-MO, zh-Hant-TW, zu, zu-ZA
+sv >> sv
+
+** test: test8288
+
+@supported=it, en
+und >> it
+und, en >> en
 
 # examples from
 # http://unicode.org/repos/cldr/tags/latest/common/bcp47/
 # http://unicode.org/repos/cldr/tags/latest/common/validity/variant.xml
 
-##################################################
-# testUnHack
+@distance=script
+und >> it
+und, en >> en
 
-en-NZ, en-IT ;         en-US ;         en-NZ
+** test: testUnHack
 
-##################################################
-# testEmptySupported => null
- ;     en ;    null
+@supported=en-NZ, en-IT
+en-US >> en-NZ
 
-##################################################
-# testVariantsAndExtensions
-##################################################
-# tests the .combine() method
+@distance=script
+en-US >> en-NZ
 
-und, fr ;      fr-BE-fonipa ;  fr ;    fr-BE-fonipa
-und, fr-CA ;   fr-BE-fonipa ;  fr-CA ;         fr-BE-fonipa
-und, fr-fonupa ;       fr-BE-fonipa ;  fr-fonupa ;     fr-BE-fonipa
-und, no ;      nn-BE-fonipa ;  no ;    no-BE-fonipa
-und, en-GB-u-sd-gbsct ;        en-fonipa-u-nu-Arab-ca-buddhist-t-m0-iso-i0-pinyin ;    en-GB-u-sd-gbsct ;      en-GB-fonipa-u-nu-Arab-ca-buddhist-t-m0-iso-i0-pinyin
+** test: testEmptySupported => null
+en >> null
 
-en-PSCRACK, de-PSCRACK, fr-PSCRACK, pt-PT-PSCRACK ;    fr-PSCRACK ;    fr-PSCRACK
-en-PSCRACK, de-PSCRACK, fr-PSCRACK, pt-PT-PSCRACK ;    fr ;    en-PSCRACK           # was: fr-PSCRACK
-en-PSCRACK, de-PSCRACK, fr-PSCRACK, pt-PT-PSCRACK ;    de-CH ;         en-PSCRACK   # was: de-PSCRACK
+# testVariantsAndExtensions
 
-##################################################
-# testClusters
+** test: tests the .combine() method
+
+@supported=und, fr
+fr-BE-fonipa >> fr | | fr-BE-fonipa
+@supported=und, fr-CA
+fr-BE-fonipa >> fr-CA | | fr-BE-fonipa
+@supported=und, fr-fonupa
+fr-BE-fonipa >> fr-fonupa | | fr-BE-fonipa
+@supported=und, no
+nn-BE-fonipa >> no | | no-BE-fonipa
+@supported=und, en-GB-u-sd-gbsct
+en-fonipa-u-nu-Arab-ca-buddhist-t-m0-iso-i0-pinyin >> en-GB-u-sd-gbsct | | en-GB-fonipa-u-nu-Arab-ca-buddhist-t-m0-iso-i0-pinyin
+
+@supported=en-PSCRACK, de-PSCRACK, fr-PSCRACK, pt-PT-PSCRACK
+fr-PSCRACK >> fr-PSCRACK
+fr >> en-PSCRACK
+de-CH >> en-PSCRACK
+
+@distance=script
+@supported=und, fr
+fr-BE-fonipa >> fr
+@supported=und, fr-CA
+fr-BE-fonipa >> fr-CA
+@supported=und, fr-fonupa
+fr-BE-fonipa >> fr-fonupa
+@supported=und, no
+nn-BE-fonipa >> no | | no-BE-fonipa
+@supported=und, en-GB-u-sd-gbsct
+en-fonipa-u-nu-Arab-ca-buddhist-t-m0-iso-i0-pinyin >> en-GB-u-sd-gbsct | | en-GB-fonipa-u-nu-Arab-ca-buddhist-t-m0-iso-i0-pinyin
+
+@supported=en-PSCRACK, de-PSCRACK, fr-PSCRACK, pt-PT-PSCRACK
+fr-PSCRACK >> fr-PSCRACK
+fr >> en-PSCRACK
+de-CH >> en-PSCRACK
+
+** test: testClusters
 # we favor es-419 over others in cluster. Clusters: es- {ES, MA, EA} {419, AR, MX}
 
-und, es, es-MA, es-MX, es-419 ;        es-AR ;         es-419
-und, es-MA, es, es-419, es-MX ;        es-AR ;         es-419
-und, es, es-MA, es-MX, es-419 ;        es-EA ;         es
-und, es-MA, es, es-419, es-MX ;        es-EA ;         es
+@supported=und, es, es-MA, es-MX, es-419
+es-AR >> es-419
+@supported=und, es-MA, es, es-419, es-MX
+es-AR >> es-419
+@supported=und, es, es-MA, es-MX, es-419
+es-EA >> es
+@supported=und, es-MA, es, es-419, es-MX
+es-EA >> es
 
 # of course, fall back to within cluster
 
-und, es, es-MA, es-MX ;        es-AR ;         es-MX
-und, es-MA, es, es-MX ;        es-AR ;         es-MX
-und, es-MA, es-MX, es-419 ;    es-EA ;         es-MA
-und, es-MA, es-419, es-MX ;    es-EA ;         es-MA
+@supported=und, es, es-MA, es-MX
+es-AR >> es-MX
+@supported=und, es-MA, es, es-MX
+es-AR >> es-MX
+@supported=und, es-MA, es-MX, es-419
+es-EA >> es-MA
+@supported=und, es-MA, es-419, es-MX
+es-EA >> es-MA
 
 # we favor es-GB over others in cluster. Clusters: en- {US, GU, VI} {GB, IN, ZA}
 
-und, en, en-GU, en-IN, en-GB ;         en-ZA ;         en-GB
-und, en-GU, en, en-GB, en-IN ;         en-ZA ;         en-GB
-und, en, en-GU, en-IN, en-GB ;         en-VI ;         en
-und, en-GU, en, en-GB, en-IN ;         en-VI ;         en
+@supported=und, en, en-GU, en-IN, en-GB
+en-ZA >> en-GB
+@supported=und, en-GU, en, en-GB, en-IN
+en-ZA >> en-GB
+@supported=und, en, en-GU, en-IN, en-GB
+en-VI >> en
+@supported=und, en-GU, en, en-GB, en-IN
+en-VI >> en
 
 # of course, fall back to within cluster
 
-und, en, en-GU, en-IN ;        en-ZA ;         en-IN
-und, en-GU, en, en-IN ;        en-ZA ;         en-IN
-und, en-GU, en-IN, en-GB ;     en-VI ;         en-GU
-und, en-GU, en-GB, en-IN ;     en-VI ;         en-GU
-
-##################################################
-# testThreshold
-@Threshold=60
-
-50, und, fr-CA-fonupa ;        fr-BE-fonipa ;  fr-CA-fonupa ;  fr-BE-fonipa
-50, und, fr-Cyrl-CA-fonupa ;   fr-BE-fonipa ;  fr-Cyrl-CA-fonupa ;     fr-Cyrl-BE-fonipa
-
-@Threshold=-1 # restore
-
-##################################################
-# testScriptFirst
-@DistanceOption=SCRIPT_FIRST
-@debug
+@supported=und, en, en-GU, en-IN
+en-ZA >> en-IN
+@supported=und, en-GU, en, en-IN
+en-ZA >> en-IN
+@supported=und, en-GU, en-IN, en-GB
+en-VI >> en-GU
+@supported=und, en-GU, en-GB, en-IN
+en-VI >> en-GU
+
+@distance=script
+@supported=und, es, es-MA, es-MX, es-419
+es-AR >> es-419
+@supported=und, es-MA, es, es-419, es-MX
+es-AR >> es-419
+@supported=und, es, es-MA, es-MX, es-419
+es-EA >> es
+@supported=und, es-MA, es, es-419, es-MX
+es-EA >> es
+
+@supported=und, es, es-MA, es-MX
+es-AR >> es-MX
+@supported=und, es-MA, es, es-MX
+es-AR >> es-MX
+@supported=und, es-MA, es-MX, es-419
+es-EA >> es-MA
+@supported=und, es-MA, es-419, es-MX
+es-EA >> es-MA
+
+@supported=und, en, en-GU, en-IN, en-GB
+en-ZA >> en-GB
+@supported=und, en-GU, en, en-GB, en-IN
+en-ZA >> en-GB
+@supported=und, en, en-GU, en-IN, en-GB
+en-VI >> en
+@supported=und, en-GU, en, en-GB, en-IN
+en-VI >> en
+
+@supported=und, en, en-GU, en-IN
+en-ZA >> en-IN
+@supported=und, en-GU, en, en-IN
+en-ZA >> en-IN
+@supported=und, en-GU, en-IN, en-GB
+en-VI >> en-GU
+@supported=und, en-GU, en-GB, en-IN
+en-VI >> en-GU
+
+** test: testThreshold
+@supported=50, und, fr-CA-fonupa
+@threshold=60
+fr-BE-fonipa >> fr-CA-fonupa | | fr-BE-fonipa
+@supported=und, fr-Cyrl-CA-fonupa
+fr-BE-fonipa >> fr-Cyrl-CA-fonupa | | fr-Cyrl-BE-fonipa
+@threshold=50
+fr-BE-fonipa >> und
+
+@distance=script
+@supported=50, und, fr-CA-fonupa
+@threshold=
+fr-BE-fonipa >> fr-CA-fonupa | | fr-BE-fonipa
+@supported=und, fr-Cyrl-CA-fonupa
+fr-BE-fonipa >> fr-Cyrl-CA-fonupa | fr-BE-fonipa
+
+** test: testScriptFirst
+@supported=ru, fr
+zh, pl >> ru
+zh-Cyrl, pl >> ru
+@supported=hr, en-Cyrl
+sr >> hr
+@supported=da, ru, hr
+sr >> da
+
+@distance=script
+@supported=ru, fr
+zh, pl >> fr
+zh-Cyrl, pl >> ru
+@supported=hr, en-Cyrl
+sr >> en-Cyrl
+@supported=da, ru, hr
+sr >> ru
+
+## III
+
+** test: testBasicsWithDefault
+@supported=en-GB, en
+@default=fr
+en-GB >> en-GB
+en-US >> en
+fr >> fr
+ja >> fr
+
+@distance=script
+en-GB >> en-GB
+en-US >> en
+fr >> en-GB
+ja >> en-GB
+
+** test: testEmptyWithDefault
+@default=en
+fr >> en
+
+** test: testGetBestMatchForList_exactMatch
+@supported=fr, en-GB, ja, es-ES, es-MX
+ja, de >> ja
+
+** test: testGetBestMatchForList_simpleVariantMatch
+# Intentionally avoiding a perfect-match or two candidates for variant matches.
+@supported=fr, en-GB, ja, es-ES, es-MX
+de, en-US >> en-GB
+# Fall back.
+de, zh >> fr
+
+** test: TestEuHack
+@supported=en-NZ, en-IT
+en-US >> en-NZ
+
+** test: TestBasics
+@supported=fr, en-GB, en
+en-GB >> en-GB
+en-US >> en
+fr-FR >> fr
+ja-JP >> fr
+# For a language that doesn't match anything, return the default.
+zu >> en-GB
+root >> fr
+
+@distance=script
+en-GB >> en-GB
+en-US >> en
+fr-FR >> fr
+ja-JP >> fr
+zu >> en-GB
+root >> en
+
+** test: TestExactMatch
+@supported=fr, en-GB, ja, es-ES, es-MX
+ja, de >> ja
+
+** test: TestSimpleVariantMatch
+@supported=fr, en-GB, ja, es-ES, es-MX
+de, en-US >> en-GB
+de, zh >> fr
+
+** test: TestMatchOnMaximized
+# ja-JP matches ja on likely subtags, and it's listed first, thus it wins
+# over the secondary preference en-GB.
+@supported=fr, en-GB, ja, es-ES, es-MX
+ja-JP, en-GB >> ja
+# Check that if the preference is maximized already, it works as well.
+ja-Jpan-JP, en-GB >> ja
+@supported=fr, zh-Hant, en
+zh, en >> en
+
+@distance=script
+zh, en >> en
+
+** test: TestCloseEnoughMatchOnMaximized
+@supported=en-GB, en, de, fr, ja
+de-CH, fr >> de
+en-US, ar, nl, de, ja >> en
+
+** test: TestGetBestMatchForPortuguese
+# 1. a supported set containing an explicit pt: {pt-PT, pt-BR, es, es-419}
+# 2. a supported set containing an implicit pt: {pt-PT, pt, es, es-419}
+# 3. a supported set containing no pt: {pt-BR, es, es-419}
+# European user who prefers Spanish over Brazilian Portuguese as a fallback.
+@supported=pt-PT, pt-BR, es, es-419
+pt-PT, es, pt >> pt-PT
+@supported=pt-PT, pt, es, es-419
+pt-PT, es, pt >> pt-PT
+@supported=pt-BR, es, es-419
+pt-PT, es, pt >> pt-BR
+
+# Brazilian user who prefers South American Spanish over European Portuguese
+# as a fallback. The asymmetry between this case and above is because it's
+# "pt-PT" that's missing between the matchers.
+@supported=pt-PT, pt-BR, es, es-419
+pt, es-419, pt-PT >> pt-BR
+@supported=pt-PT, pt, es, es-419
+pt, es-419, pt-PT >> pt
+@supported=pt-BR, es, es-419
+pt, es-419, pt-PT >> pt-BR
+
+# Sometimes we get "pt-US" for a user's language (which CLDR doesn't
+# recognize) but we deal with that as a synonym for "pt-BR".
+@supported=pt-PT, pt-BR, es, es-419
+pt-US, pt-PT >> pt-BR
+@supported=pt-PT, pt, es, es-419
+pt-US, pt-PT >> pt
+
+@distance=script
+@supported=pt-BR, es, es-419
+pt-PT, es, pt >> pt-BR
+@supported=pt-PT, pt, es, es-419
+pt-US, pt-PT >> pt
+
+** test: TestScriptAndRegion
+@supported=en-GB, en
+en-CA >> en-GB
+# fr-CA is a "close enough" match to "fr" to be returned in favor of "en-GB"
+@supported=fr, en-GB, en
+fr-CA, en-CA >> fr
+@supported=zh-Hant, zh-TW
+zh-HK >> zh-Hant
+
+@distance=script
+@supported=en-GB, en
+en-CA >> en-GB
+@supported=fr, en-GB, en
+fr-CA, en-CA >> fr
+@supported=zh-Hant, zh-TW
+zh-HK >> zh-Hant
+
+** test: TestFallback
+@supported=zh-CN, zh-TW, iw
+zh-Hant >> zh-TW
+zh >> zh-CN
+zh-Hans-CN >> zh-CN
+zh-Hant-HK >> zh-TW
+he-IT >> iw
+
+** test: TestFallbackWithDefault
+# Check that script fallbacks are handled right and that we don't have to
+# fall back to the default.
+@supported=zh-CN, zh-TW, iw
+@default=fr
+zh-Hant >> zh-TW
+zh >> zh-CN
+zh-Hans-CN >> zh-CN
+zh-Hant-HK >> zh-TW
+he-IT >> iw
+
+@distance=script
+zh-Hant >> zh-TW
+zh >> zh-CN
+zh-Hans-CN >> zh-CN
+zh-Hant-HK >> zh-TW
+he-IT >> iw
+
+** test: TestSpecials
+# Check that nearby languages are handled.
+@supported=en, fil, ro, nn
+tl >> fil
+mo >> ro
+nb >> nn
+ja >> en # Make sure default works.
+
+** test: TestRegionalSpecials
+# Verify that en-AU is closer to en-GB than to en (which is en-US).
+@supported=en, en-GB, es-ES, es-419
+en-AU >> en-GB
+# Following 2 cases test closer/smaller region difference.
+es-MX >> es-419
+es-PT >> es-ES
+
+@distance=script
+en-AU >> en-GB
+es-MX >> es-419
+es-PT >> es-ES
+
+** test: TestEmpty
+fr >> null
+
+** test: TestUndefined
+# When the undefined language doesn't match anything in the list,
+# return the default.
+@supported=it, fr
+und >> it
+# When it *does* occur in the list, return it.
+@supported=it, und
+und >> und
+# The unusual part:
+# max("und") = "en-Latn-US", and since matching is based on
+# maximized tags, the undefined language would normally match
+# English.  But that would produce the counterintuitive results
+# that BestMatchFor("und", LanguageMatcher("it,en")) would be "en",
+# and BestMatchFor("en", LanguageMatcher("it,und")) would be "und".
 
-ru, fr ; zh, pl ; fr
-ru, fr ; zh-Cyrl, pl ; ru
-#hr, en-Cyrl; sr ; en-Cyrl
-da, ru, hr; sr ; ru
+# To avoid that, we change the matcher's definitions of max
+# (AddLikelySubtagsWithDefaults) so that max("und")="und". That
+# produces the following, more desirable results:
+@supported=it, en
+und >> it
+@supported=it, und
+en >> it
+
+** test: TestVariantWithScriptMatch
+@supported=fr, en, sv
+en-GB >> en
+en-GB, sv >> en
+
+@distance=script
+en-GB, sv >> en
+
+** test: Serbian
+@supported=und, sr
+sr-ME >> sr
+@supported=und, sr-ME
+sr >> sr-ME
+@supported=und, sr-Latn
+bs >> und
+@supported=und, bs
+sr-Latn >> und
+@supported=und, sr
+bs >> und
+@supported=und, bs
+sr >> und
+@supported=und, sr-Latn
+sr >> sr-Latn
+@supported=und, sr
+sr-Latn >> sr
+
+@distance=script
+sr-ME >> sr
+@supported=und, sr-ME
+sr >> sr-ME
+@supported=und, sr-Latn
+bs >> sr-Latn
+@supported=und, bs
+sr-Latn >> bs
+@supported=und, sr
+bs >> und
+@supported=und, bs
+sr >> und
+@supported=und, sr-Latn
+sr >> sr-Latn
+@supported=und, sr
+sr-Latn >> sr
+
+** test: MatchGooglePrivateUseSubtag
+@supported=fr, x-bork, en-Latn-US
+x-piglatin >> fr
+x-bork >> x-bork
+@supported=fr, en-GB, x-bork, es-ES, es-419
+x-piglatin >> fr
+x-bork >> x-bork
+
+@distance=script
+@supported=fr, x-bork, en-Latn-US
+x-piglatin >> x-bork
+x-bork >> x-bork
+@supported=fr, en-GB, x-bork, es-ES, es-419
+x-piglatin >> x-bork
+x-bork >> x-bork
+
+** test: MatchGrandfatheredCode
+@supported=fr, i-klingon, en-Latn-US
+en-GB-oed >> en-Latn-US
+i-klingon >> tlh
+
+@distance=script
+en-GB-oed >> en-Latn-US
+i-klingon >> tlh
+
+** test: MatchGooglePseudoLocale
+# Google pseudo locales using variant subtags.
+# (See below for the region code based pseudo locales.)
+@supported=fr, en-PSACCENT, ar-PSBIDI, en-PSCRACK, zh-Hans-PSCRACK, pt-PT-PSCRACK, pt
+de >> fr
+en-US >> fr
+en >> fr
+ar-PSBIDI >> ar-PSBIDI
+en-PSACCENT >> en-PSACCENT
+en-PSCRACK >> en-PSCRACK
+pt-BR >> pt
+pt-PT-PSCRACK >> pt-PT-PSCRACK
+zh-Hans-PSCRACK >> zh-Hans-PSCRACK
+
+@distance=script
+de >> fr
+en-US >> fr
+en >> fr
+ar-PSBIDI >> ar-PSBIDI
+en-PSACCENT >> en-PSACCENT
+en-PSCRACK >> en-PSCRACK
+pt-BR >> pt
+pt-PT-PSCRACK >> pt-PT-PSCRACK
+zh-Hans-PSCRACK >> zh-Hans-PSCRACK
+
+** test: MatchGooglePseudoLocaleWithFallbacks
+# Pseudo locales based on the fall back option (XA..XC region codes).
+@supported=fr, en-XA, ar-XB, en-XC, zh-Hans-XC, pt
+de >> fr
+en-US >> fr
+en >> fr
+ar-XB >> ar-XB
+en-XA >> en-XA
+en-XC >> en-XC
+pt-BR >> pt
+zh-Hans-XC >> zh-Hans-XC
+
+@distance=script
+de >> fr
+en-US >> fr
+en >> fr
+ar-XB >> ar-XB
+en-XA >> en-XA
+en-XC >> en-XC
+pt-BR >> pt
+zh-Hans-XC >> zh-Hans-XC
+
+** test: DoNotMatchGooglePseudoLocale
+@supported=fr, en-XA, ar-XB, en-PSACCENT, ar-PSBIDI, en-DE, pt, ar-SY, ar-PSCRACK
+de >> fr
+# We wouldn't want to return pseudo locales when there's a good match for an
+# ordinary locale.
+# Note: If LanguageMatcher was not aware of PSACCENT, it would consider the
+# distance from "en" to "en-PSACCENT" smaller than to "en-DE" (the standard
+# variant distance is smaller than a region distance).
+en >> en-DE
+ar-EG >> ar-SY
+pt-BR >> pt
+ar-XB >> ar-XB
+ar-PSBIDI >> ar-PSBIDI
+en-XA >> en-XA
+en-PSACCENT >> en-PSACCENT
+ar-PSCRACK >> ar-PSCRACK
+
+@distance=script
+de >> en-DE
+en >> en-DE
+ar-EG >> ar-SY
+pt-BR >> pt
+ar-XB >> ar-XB
+ar-PSBIDI >> ar-PSBIDI
+en-XA >> en-XA
+en-PSACCENT >> en-PSACCENT
+ar-PSCRACK >> ar-PSCRACK
+
+** test: BestMatchForTraditionalChinese
+# Scenario: An application that only supports Simplified Chinese (and some
+# other languages), but does not support Traditional Chinese. zh-Hans-CN
+# could be replaced with zh-CN, zh, or zh-Hans, it wouldn't make much of a
+# difference.
+# The script distance (simplified vs. traditional Han) is considered small
+# enough to be an acceptable match. The regional difference is considered
+# almost insignificant.
+@supported=fr, zh-Hans-CN, en-US
+zh-TW >> zh-Hans-CN
+zh-Hant >> zh-Hans-CN
+
+# For geopolitical reasons, you might want to avoid a zh-Hant -> zh-Hans
+# match. In this case, if zh-TW, zh-HK or a tag starting with zh-Hant is
+# requested, you can change your call to getBestMatch to include a 2nd
+# language preference. "en" is a better match since its distance to "en-US"
+# is closer than the distance from "zh-TW" to "zh-CN" (script distance).
+zh-TW, en >> en-US
+zh-Hant-CN, en >> en-US
+zh-Hans, en >> zh-Hans-CN
+
+** test: MaxBeforeEquals
+# Compare maximized forms of earlier items before testing equality
+# of later items.
+@supported=en, fr-CA
+en-US, fr-CA >> en
+
+@distance=script
+en-US, fr-CA >> en
+
+** test: SiblingDefaultRegion
+@supported=de-AT, de-DE, de-CH
+de >> de-DE
+
+** test: ReturnDefaultInsteadOfNullForEmptyPriorityList
+@default=und
+de >> und
+
+** test: ReturnSpecifiedDefaultForNoMatch
+@supported=de, en, fr
+@default=und
+hi >> und
+
+@distance=script
+hi >> de
+
+** test: MatchedLanguageIgnoresDefault
+@supported=de, en, fr
+@default=und
+fr >> fr
+
+@distance=script
+fr >> fr
+
+## GenX
+
+** test: TwoSpanishes
+@supported=es, es-MX
+@default=und
+es-001 >> es
+und >> und
+ca >> und
+gl-ES >> es
+es >> es
+es-MX >> es-MX
+es-002 >> es
+es-003 >> es-MX
+es-005 >> es-MX
+es-019 >> es-MX
+es-029 >> es-MX
+es-419 >> es-MX
+es-142 >> es
+es-150 >> es
+es-AD >> es
+es-AR >> es-MX
+es-BO >> es-MX
+es-BZ >> es-MX
+es-CA >> es-MX
+es-CL >> es-MX
+es-CO >> es-MX
+es-CR >> es-MX
+es-CU >> es-MX
+es-DO >> es-MX
+es-EC >> es-MX
+es-ES >> es
+es-GI >> es
+es-GQ >> es
+es-GT >> es-MX
+es-HN >> es-MX
+es-NI >> es-MX
+es-PA >> es-MX
+es-PE >> es-MX
+es-PH >> es
+es-PR >> es-MX
+es-PY >> es-MX
+es-SV >> es-MX
+es-US >> es-MX
+es-UY >> es-MX
+es-VE >> es-MX
+
+@distance=script
+es-001 >> es
+und >> es
+ca >> es
+gl-ES >> es
+es >> es
+es-MX >> es-MX
+es-002 >> es
+es-003 >> es-MX
+es-005 >> es-MX
+es-019 >> es-MX
+es-029 >> es-MX
+es-419 >> es-MX
+es-142 >> es
+es-150 >> es
+es-AD >> es
+es-AR >> es-MX
+es-BO >> es-MX
+es-BZ >> es-MX
+es-CA >> es-MX
+es-CL >> es-MX
+es-CO >> es-MX
+es-CR >> es-MX
+es-CU >> es-MX
+es-DO >> es-MX
+es-EC >> es-MX
+es-ES >> es
+es-GI >> es
+es-GQ >> es
+es-GT >> es-MX
+es-HN >> es-MX
+es-NI >> es-MX
+es-PA >> es-MX
+es-PE >> es-MX
+es-PH >> es
+es-PR >> es-MX
+es-PY >> es-MX
+es-SV >> es-MX
+es-US >> es-MX
+es-UY >> es-MX
+es-VE >> es-MX
+
+** test: Three Spanishes
+@supported=es, es-419, es-MX
+@default=und
+es-001 >> es
+und >> und
+ca >> und
+gl-ES >> es
+es >> es
+es-419 >> es-419
+es-002 >> es
+es-003 >> es-419
+es-005 >> es-419
+es-019 >> es-419
+es-029 >> es-419
+es-142 >> es
+es-150 >> es
+es-AD >> es
+es-AR >> es-419
+es-BO >> es-419
+es-BZ >> es-419
+es-CA >> es-419
+es-CL >> es-419
+es-CO >> es-419
+es-CR >> es-419
+es-CU >> es-419
+es-DO >> es-419
+es-EC >> es-419
+es-ES >> es
+es-GI >> es
+es-GQ >> es
+es-GT >> es-419
+es-HN >> es-419
+es-MX >> es-MX
+es-NI >> es-419
+es-PA >> es-419
+es-PE >> es-419
+es-PH >> es
+es-PR >> es-419
+es-PY >> es-419
+es-SV >> es-419
+es-US >> es-419
+es-UY >> es-419
+es-VE >> es-419
+
+@distance=script
+es-001 >> es
+und >> es
+ca >> es
+gl-ES >> es
+es >> es
+es-419 >> es-419
+es-002 >> es
+es-003 >> es-419
+es-005 >> es-419
+es-019 >> es-419
+es-029 >> es-419
+es-142 >> es
+es-150 >> es
+es-AD >> es
+es-AR >> es-419
+es-BO >> es-419
+es-BZ >> es-419
+es-CA >> es-419
+es-CL >> es-419
+es-CO >> es-419
+es-CR >> es-419
+es-CU >> es-419
+es-DO >> es-419
+es-EC >> es-419
+es-ES >> es
+es-GI >> es
+es-GQ >> es
+es-GT >> es-419
+es-HN >> es-419
+es-MX >> es-MX
+es-NI >> es-419
+es-PA >> es-419
+es-PE >> es-419
+es-PH >> es
+es-PR >> es-419
+es-PY >> es-419
+es-SV >> es-419
+es-US >> es-419
+es-UY >> es-419
+es-VE >> es-419
+
+** test: Englishes
+@supported=en-GB, en-US
+@default=und
+und >> und
+ja >> und
+fr-CA >> und
+
+# Great Britain fallback
+en-AU >> en-GB
+en-BZ >> en-GB
+en-CA >> en-GB
+en-IN >> en-GB
+en-IE >> en-GB
+en-JM >> en-GB
+en-NZ >> en-GB
+en-PK >> en-GB
+en-TT >> en-GB
+en-ZA >> en-GB
+
+# United States fallback
+en-US >> en-US
+en >> en-US
+
+@distance=script
+und >> en-GB
+ja >> en-GB
+fr-CA >> en-GB
+en-AU >> en-GB
+en-BZ >> en-GB
+en-CA >> en-GB
+en-IN >> en-GB
+en-IE >> en-GB
+en-JM >> en-GB
+en-NZ >> en-GB
+en-PK >> en-GB
+en-TT >> en-GB
+en-ZA >> en-GB
+en-US >> en-US
+en >> en-US
+
+** test: TestFallback
+# manyEnMatcher
+@supported=en-GB, en-US, en, en-AU
+@default=und
+und >> und
+ja >> und
+fr-CA >> und
+
+# nonUsMatcher
+fr >> und
+
+# onlyAuMatcher
+@supported=en-AU, ja, ca
+fr >> und
+
+# noEnMatcher
+@supported=pl, ja, ca
+fr >> und
+
+@distance=script
+@supported=en-GB, en-US, en, en-AU
+und >> en-GB
+ja >> en-GB
+fr-CA >> en-GB
+fr >> en-GB
+@supported=en-AU, ja, ca
+fr >> en-AU
+@supported=pl, ja, ca
+fr >> pl
+
+## Go
+
+** test: basics
+@supported=fr, en-GB, en
+en-GB >> en-GB
+en-US >> en
+fr-FR >> fr
+ja-JP >> fr
+
+** test: script fallbacks
+@supported=zh-CN, zh-TW, iw
+zh-Hant >> zh-TW
+zh >> zh-CN
+zh-Hans-CN >> zh-CN
+zh-Hant-HK >> zh-TW
+@default=iw
+he-IT >> iw
+
+@distance=script
+he-IT >> iw
+
+** test: language-specific script fallbacks 1
+@supported=en, sr, nl
+sr-Latn >> sr
+sh >> en
+hr >> en
+bs >> en
+nl-Cyrl >> en # Mark: Expected value should be en not sr. Script difference exceeds threshold, so can't be nl
+
+@distance=script
+sr-Latn >> sr
+hr >> en
+bs >> en
+nl-Cyrl >> sr
+
+** test: language-specific script fallbacks 2
+@supported=en, sr-Latn
+sr >> sr-Latn
+sr-Cyrl >> sr-Latn
+@default=und
+hr >> und
+
+@distance=script
+@default=
+sr >> sr-Latn
+sr-Cyrl >> sr-Latn
+@default=und
+hr >> en
+
+** test: don't match hr to sr-Latn
+@supported=en, sr-Latn
+hr >> en
+
+@distance=script
+hr >> en
+
+** test: both deprecated and not
+@supported=fil, tl, iw, he
+he-IT >> iw
+he >> he
+iw >> iw
+fil-IT >> fil
+fil >> fil
+tl >> tl
+
+@distance=script
+he-IT >> iw
+he >> he
+iw >> iw
+fil-IT >> fil
+fil >> fil
+tl >> tl
+
+** test: nearby languages: Nynorsk to Bokmål
+@supported=en, nb
+nn >> nb
+
+@distance=script
+nn >> nb
+
+** test: nearby languages: Danish does not match nn
+@supported=en, nn
+da >> en
+
+@distance=script
+da >> en
+
+** test: nearby languages: Danish matches no
+@supported=en, no
+da >> no
+
+@distance=script
+da >> no
+
+** test: nearby languages: Danish matches nb
+@supported=en, nb
+da >> nb
+
+** test: prefer matching languages over language variants.
+@supported=nn, en-GB
+no, en-US >> nn
+nb, en-US >> nn
+
+@distance=script
+no, en-US >> nn
+nb, en-US >> nn
+
+** test: deprecated version is closer than same language with other differences
+@supported=nl, he, en-GB
+iw, en-US >> he
+
+@distance=script
+iw, en-US >> he
+
+** test: macro equivalent is closer than same language with other differences
+@supported=nl, zh, en-GB, no
+cmn, en-US >> zh
+nb, en-US >> no
+
+@distance=script
+cmn, en-US >> zh
+nb, en-US >> no
+
+** test: legacy equivalent is closer than same language with other differences
+@supported=nl, fil, en-GB
+tl, en-US >> fil
+
+@distance=script
+tl, en-US >> fil
+
+** test: distinguish near equivalents
+@supported=en, ro, mo, ro-MD
+ro >> ro
+mo >> mo
+ro-MD >> ro-MD
+
+@distance=script
+ro >> ro
+mo >> mo
+ro-MD >> ro-MD
+
+** test: maximization of legacy
+@supported=sr-Cyrl, sr-Latn, ro, ro-MD
+sh >> sr-Latn
+mo >> ro
+
+@distance=script
+sh >> sr-Latn
+mo >> ro
+
+** test: empty
+fr >> null
+en >> null
+
+** test: private use subtags
+@supported=fr, en-GB, x-bork, es-ES, es-419
+x-piglatin >> fr
+x-bork >> x-bork
+
+** test: grandfathered codes
+@supported=fr, i-klingon, en-Latn-US
+en-GB-oed >> en-Latn-US
+i-klingon >> tlh
+
+
+** test: simple variant match
+@supported=fr, en-GB, ja, es-ES, es-MX
+de, en-US >> en-GB
+de, zh >> fr
+
+** test: best match for traditional Chinese
+@supported=fr, zh-Hans-CN, en-US
+zh-TW >> zh-Hans-CN
+zh-Hant >> zh-Hans-CN
+zh-TW, en >> en-US
+zh-Hant-CN, en >> en-US
+zh-Hans, en >> zh-Hans-CN
+
+** test: more specific script should win in case regions are identical
+@supported=af, af-Latn, af-Arab
+af >> af
+af-ZA >> af
+af-Latn-ZA >> af
+af-Latn >> af-Latn
+
+@distance=script
+af >> af
+af-ZA >> af
+af-Latn-ZA >> af
+af-Latn >> af-Latn
+
+** test: more specific region should win
+@supported=nl, nl-NL, nl-BE
+nl >> nl
+nl-Latn >> nl
+nl-Latn-NL >> nl
+nl-NL >> nl-NL
+
+@distance=script
+nl >> nl
+nl-Latn >> nl
+nl-Latn-NL >> nl
+nl-NL >> nl-NL
+
+** test: region may replace matched if matched is enclosing
+@supported=es-419, es
+@default=es-MX
+es-MX >> es-419
+@default=
+es-SG >> es
+
+@distance=script
+@default=es-MX
+es-MX >> es-419
+@default=
+es-SG >> es
+
+** test: more specific region wins over more specific script
+@supported=nl, nl-Latn, nl-NL, nl-BE
+nl >> nl
+nl-Latn >> nl-Latn
+nl-NL >> nl-NL
+nl-Latn-NL >> nl
+
+@distance=script
+nl >> nl
+nl-Latn >> nl-Latn
+nl-NL >> nl-NL
+nl-Latn-NL >> nl
+
+** test: region distance Portuguese
+@supported=pt, pt-PT
+pt-ES >> pt-PT
+
+@distance=script
+pt-ES >> pt-PT
+
+** test: if no preferred locale specified, pick top language, not regional
+@supported=en, fr, fr-CA, fr-CH
+fr-US >> fr
+
+@distance=script
+fr-US >> fr
+
+** test: region distance German
+@supported=de-AT, de-DE, de-CH
+de >> de-DE
+
+** test: en-AU is closer to en-GB than to en (which is en-US)
+@supported=en, en-GB, es-ES, es-419
+en-AU >> en-GB
+@default=es-MX
+es-MX >> es-419
+@default=
+es-PT >> es-ES
+
+@distance=script
+en-AU >> en-GB
+es-MX >> es-419
+@default=
+es-PT >> es-ES
+
+** test: undefined
+@supported=it, fr
+und >> it
+
+** test: und does not match en
+@supported=it, en
+und >> it
+
+** test: undefined in priority list
+@supported=it, und
+und >> und
+en >> it
+
+** test: undefined
+@supported=it, fr, zh
+und-FR >> fr
+und-CN >> zh
+und-Hans >> zh
+und-Hant >> zh
+und-Latn >> it
+
+@distance=script
+und-FR >> fr
+und-CN >> zh
+und-Hans >> zh
+und-Hant >> zh
+und-Latn >> it
+
+** test: match on maximized tag
+@supported=fr, en-GB, ja, es-ES, es-MX
+ja-JP, en-GB >> ja
+ja-Jpan-JP, en-GB >> ja
+
+** test: pick best maximized tag
+@supported=ja, ja-Jpan-US, ja-JP, en, ru
+ja-Jpan, ru >> ja
+ja-JP, ru >> ja-JP
+ja-US, ru >> ja-Jpan-US
+
+@distance=script
+ja-Jpan, ru >> ja
+ja-JP, ru >> ja-JP
+ja-US, ru >> ja-Jpan-US
+
+** test: termination: pick best maximized match
+@supported=ja, ja-Jpan, ja-JP, en, ru
+ja-Jpan-JP, ru >> ja
+ja-Jpan, ru >> ja-Jpan
+
+@distance=script
+ja-Jpan-JP, ru >> ja
+ja-Jpan, ru >> ja-Jpan
+
+** test: same language over exact, but distinguish when user is explicit
+@supported=fr, en-GB, ja, es-ES, es-MX
+ja, de >> ja
+@supported=en, de, fr, ja
+de-CH, fr >> de
+@supported=en-GB, nl
+en, nl >> en-GB
+en, nl, en-GB >> en-GB
+
+@distance=script
+@supported=fr, en-GB, ja, es-ES, es-MX
+ja, de >> ja
+@supported=en, de, fr, ja
+de-CH, fr >> de
+@supported=en-GB, nl
+en, nl >> en-GB
+en, nl, en-GB >> en-GB
+
+** test: parent relation preserved
+@supported=en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK
+en-150 >> en-GB
+en-AU >> en-GB
+en-BE >> en-GB
+en-GG >> en-GB
+en-GI >> en-GB
+en-HK >> en-GB
+en-IE >> en-GB
+en-IM >> en-GB
+en-IN >> en-GB
+en-JE >> en-GB
+en-MT >> en-GB
+en-NZ >> en-GB
+en-PK >> en-GB
+en-SG >> en-GB
+en-DE >> en-GB
+@default=es-AR
+es-AR >> es-419
+@default=es-BO
+es-BO >> es-419
+@default=es-CL
+es-CL >> es-419
+@default=es-CO
+es-CO >> es-419
+@default=es-CR
+es-CR >> es-419
+@default=es-CU
+es-CU >> es-419
+@default=es-DO
+es-DO >> es-419
+@default=es-EC
+es-EC >> es-419
+@default=es-GT
+es-GT >> es-419
+@default=es-HN
+es-HN >> es-419
+@default=es-MX
+es-MX >> es-419
+@default=es-NI
+es-NI >> es-419
+@default=es-PA
+es-PA >> es-419
+@default=es-PE
+es-PE >> es-419
+@default=es-PR
+es-PR >> es-419
+@default=
+es-PT >> es
+@default=es-PY
+es-PY >> es-419
+@default=es-SV
+es-SV >> es-419
+@default=
+es-US >> es-419
+@default=es-UY
+es-UY >> es-419
+@default=es-VE
+es-VE >> es-419
+@default=
+pt-AO >> pt-PT
+pt-CV >> pt-PT
+pt-GW >> pt-PT
+pt-MO >> pt-PT
+pt-MZ >> pt-PT
+pt-ST >> pt-PT
+pt-TL >> pt-PT
+
+@distance=script
+en-150 >> en-GB
+en-AU >> en-GB
+en-BE >> en-GB
+en-GG >> en-GB
+en-GI >> en-GB
+en-HK >> en-GB
+en-IE >> en-GB
+en-IM >> en-GB
+en-IN >> en-GB
+en-JE >> en-GB
+en-MT >> en-GB
+en-NZ >> en-GB
+en-PK >> en-GB
+en-SG >> en-GB
+en-DE >> en-GB
+@default=es-AR
+es-AR >> es-419
+@default=es-BO
+es-BO >> es-419
+@default=es-CL
+es-CL >> es-419
+@default=es-CO
+es-CO >> es-419
+@default=es-CR
+es-CR >> es-419
+@default=es-CU
+es-CU >> es-419
+@default=es-DO
+es-DO >> es-419
+@default=es-EC
+es-EC >> es-419
+@default=es-GT
+es-GT >> es-419
+@default=es-HN
+es-HN >> es-419
+@default=es-MX
+es-MX >> es-419
+@default=es-NI
+es-NI >> es-419
+@default=es-PA
+es-PA >> es-419
+@default=es-PE
+es-PE >> es-419
+@default=es-PR
+es-PR >> es-419
+@default=
+es-PT >> es
+@default=es-PY
+es-PY >> es-419
+@default=es-SV
+es-SV >> es-419
+@default=
+es-US >> es-419
+@default=es-UY
+es-UY >> es-419
+@default=es-VE
+es-VE >> es-419
+@default=
+pt-AO >> pt-PT
+pt-CV >> pt-PT
+pt-GW >> pt-PT
+pt-MO >> pt-PT
+pt-MZ >> pt-PT
+pt-ST >> pt-PT
+pt-TL >> pt-PT
+
+** test: preserve extensions
+@supported=en, de, sl-NEDIS
+@default=de-u-co-phonebk
+de-FR-u-co-phonebk >> de
+@default=sl-NEDIS-u-cu-eur
+sl-NEDIS-u-cu-eur >> sl-NEDIS
+sl-u-cu-eur >> sl-NEDIS
+sl-HR-NEDIS-u-cu-eur >> sl-NEDIS
+@default=de-t-m0-iso-i0-pinyin
+de-t-m0-iso-i0-pinyin >> de
+
+@distance=script
+@default=de-u-co-phonebk
+de-FR-u-co-phonebk >> de
+@default=sl-NEDIS-u-cu-eur
+sl-NEDIS-u-cu-eur >> sl-NEDIS
+sl-u-cu-eur >> sl-NEDIS
+sl-HR-NEDIS-u-cu-eur >> sl-NEDIS
+@default=de-t-m0-iso-i0-pinyin
+de-t-m0-iso-i0-pinyin >> de
+
+## ULS
+
+** test: testEmptyUserLanguagesGetsEmpty_getBestMatches
+@supported=de
+ >> de
+
+** test: testNoStrongMatchGetsEmpty_getBestMatches
+@supported=de
+fr >> de
+
+@distance=script
+fr >> de
+
+** test: testLooseMatchForGeneral_getBestMatches
+@supported=es-419
+es-MX >> es-419
+
+@distance=script
+es-MX >> es-419
+
+** test: testLooseMatchForEnglish_getBestMatches
+@supported=en, en-GB
+en-CA >> en-GB
+
+@distance=script
+en-CA >> en-GB
+
+** test: testLooseMatchForChinese_getBestMatches
+@supported=zh
+zh-TW >> zh
+
+@distance=script
+zh-TW >> zh
+
+## Geo
+
+** test: testGetBestMatchWithMinMatchScore
+@supported=fr-FR, fr, fr-CA, en
+@default=und
+fr >> fr # Exact match is chosen.
+@supported=en, fr, fr-CA
+fr-FR >> fr # Parent match is chosen.
+@supported=en, fr-CA
+fr-FR >> fr-CA # Sibling match is chosen.
+@supported=fr-CA, fr-FR
+fr >> fr-FR # Inferred region match is chosen.
+fr-SN >> fr-CA
+@supported=en, fr-FR
+fr >> fr-FR # Child match is chosen.
+@supported=de, en, it
+fr >> und
+@supported=iw, en
+iw-Latn >> und
+@supported=iw, no
+ru >> und
+@supported=iw-Latn, iw-Cyrl, iw
+ru >> und
+@supported=iw, iw-Latn
+ru >> und
+en >> und
+@supported=en, uk
+ru >> und
+@supported=zh-TW, en
+zh-CN >> zh-TW
+@supported=ja
+ru >> und
+
+@distance=script
+@supported=fr-FR, fr, fr-CA, en
+fr >> fr
+@supported=en, fr, fr-CA
+fr-FR >> fr
+@supported=en, fr-CA
+fr-FR >> fr-CA
+@supported=fr-CA, fr-FR
+fr >> fr-FR
+fr-SN >> fr-CA
+@supported=en, fr-FR
+fr >> fr-FR
+@supported=de, en, it
+fr >> de
+@supported=iw, en
+iw-Latn >> en
+@supported=iw, no
+ru >> iw
+@supported=iw-Latn, iw-Cyrl, iw
+ru >> iw-Cyrl
+@supported=iw, iw-Latn
+ru >> iw
+en >> iw-Latn
+@supported=en, uk
+ru >> uk
+@supported=zh-TW, en
+zh-CN >> zh-TW
+@supported=ja
+ru >> ja