Fix incorrect regex in WebView useragent test.

The test was updated to make the useragent more flexible but the new
regex incorrectly expected a semicolon inbetween the model and build ID,
which isn't supposed to be there.

Fix the regular expression to require the correct spacing/semicolons;
this requires a more complex regex, so comment the regex and also add
tests for the test itself with known-good/bad forms of useragent.

Fixes: 125402818
Change-Id: Iea63668835a399e7feffc9af8c3aef6488ab1fbe
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index f52c28e..63b44b9 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -104,7 +104,7 @@
      * compatibility definition (tokens in angle brackets are variables, tokens in square
      * brackets are optional):
      * <p/>
-     * Mozilla/5.0 (Linux; Android <version>; [<devicemodel>;] [Build/<buildID>;] wv)
+     * Mozilla/5.0 (Linux; Android <version>; [<devicemodel>] [Build/<buildID>]; wv)
      * AppleWebKit/<major>.<minor> (KHTML, like Gecko) Version/<major>.<minor>
      * Chrome/<major>.<minor>.<branch>.<build>[ Mobile] Safari/<major>.<minor>
      */
@@ -112,36 +112,83 @@
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
         }
-        final String actualUserAgentString = mSettings.getUserAgentString();
-        Log.i(LOG_TAG, String.format("Checking user agent string %s", actualUserAgentString));
+        checkUserAgentStringHelper(mSettings.getUserAgentString(), true);
+    }
 
-        String expectedRelease, expectedModel;
+    /**
+     * Verifies that the useragent testing regex is actually correct, because it's very complex.
+     */
+    public void testUserAgentStringTest() {
+        // All test UAs share the same prefix and suffix; only the middle part varies.
+        final String prefix = "Mozilla/5.0 (Linux; Android " + Build.VERSION.RELEASE + "; ";
+        final String suffix = "wv) AppleWebKit/0.0 (KHTML, like Gecko) Version/4.0 Chrome/0.0.0.0 Safari/0.0";
+
+        // Valid cases:
+        // Both model and build present
+        checkUserAgentStringHelper(prefix + Build.MODEL + " Build/" + Build.ID + "; " + suffix, true);
+        // Just model
+        checkUserAgentStringHelper(prefix + Build.MODEL + "; " + suffix, true);
+        // Just build
+        checkUserAgentStringHelper(prefix + "Build/" + Build.ID + "; " + suffix, true);
+        // Neither
+        checkUserAgentStringHelper(prefix + suffix, true);
+
+        // Invalid cases:
+        // No space between model and build
+        checkUserAgentStringHelper(prefix + Build.MODEL + "Build/" + Build.ID + "; " + suffix, false);
+        // No semicolon after model and/or build
+        checkUserAgentStringHelper(prefix + Build.MODEL + " Build/" + Build.ID + suffix, false);
+        checkUserAgentStringHelper(prefix + Build.MODEL + suffix, false);
+        checkUserAgentStringHelper(prefix + "Build/" + Build.ID + suffix, false);
+        // Double semicolon when both omitted
+        checkUserAgentStringHelper(prefix + "; " + suffix, false);
+    }
+
+    /**
+     * Helper function to validate that a given useragent string is or is not valid.
+     */
+    private void checkUserAgentStringHelper(final String useragent, boolean shouldMatch) {
+        String expectedRelease;
         if ("REL".equals(Build.VERSION.CODENAME)) {
             expectedRelease = Pattern.quote(Build.VERSION.RELEASE);
-            expectedModel = Pattern.quote(Build.MODEL);
         } else {
-            // Non-release builds don't include real release version/model, be lenient.
-            expectedRelease = expectedModel = "[^;]+";
+            // Non-release builds don't include real release version, be lenient.
+            expectedRelease = "[^;]+";
         }
 
         // Build expected regex inserting the appropriate variables, as this is easier to
         // understand and get right than matching any possible useragent and comparing the
         // variables afterward.
         final String patternString =
-                Pattern.quote("Mozilla/5.0 (Linux; Android ") + expectedRelease + "; " +
-                "(" + expectedModel + "; )?" +                  // Optional
-                "(Build/" + Pattern.quote(Build.ID) + "; )?" +  // Optional
-                Pattern.quote("wv) ") +
+                // Release version always has a semicolon after it:
+                Pattern.quote("Mozilla/5.0 (Linux; Android ") + expectedRelease + ";" +
+                // Model is optional, but if present must have a space first:
+                "( " + Pattern.quote(Build.MODEL) + ")?" +
+                // Build is optional, but if present must have a space first:
+                "( Build/" + Pattern.quote(Build.ID) + ")?" +
+                // We want a semicolon before the wv token, but we don't want to have two in a row
+                // if both model and build are omitted. Lookbehind assertions ensure either:
+                // - the previous character is a semicolon
+                // - or the previous character is NOT a semicolon AND a semicolon is added here.
+                "((?<=;)|(?<!;);)" +
+                // After that we can just check for " wv)" to finish the platform section:
+                Pattern.quote(" wv) ") +
+                // The rest of the expression is browser tokens and is fairly simple:
                 "AppleWebKit/\\d+\\.\\d+ " +
                 Pattern.quote("(KHTML, like Gecko) Version/4.0 ") +
                 "Chrome/\\d+\\.\\d+\\.\\d+\\.\\d+ " +
                 "(Mobile )?Safari/\\d+\\.\\d+";
-        Log.i(LOG_TAG, String.format("Trying to match pattern %s", patternString));
         final Pattern userAgentExpr = Pattern.compile(patternString);
-        Matcher patternMatcher = userAgentExpr.matcher(actualUserAgentString);
-        assertTrue(String.format("User agent string did not match expected pattern. \nExpected " +
-                        "pattern:\n%s\nActual:\n%s", patternString, actualUserAgentString),
-                        patternMatcher.find());
+        Matcher patternMatcher = userAgentExpr.matcher(useragent);
+        if (shouldMatch) {
+            assertTrue(String.format("User agent string did not match expected pattern. \n" +
+                            "Expected pattern:\n%s\nActual:\n%s", patternString, useragent),
+                    patternMatcher.find());
+        } else {
+            assertFalse(String.format("Known-bad user agent string incorrectly matched. \n" +
+                            "Expected pattern:\n%s\nActual:\n%s", patternString, useragent),
+                    patternMatcher.find());
+        }
     }
 
     public void testAccessUserAgentString() throws Exception {