Home | History | Annotate | Download | only in test
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/file_version_info.h"
      6 #include "base/file_version_info_win.h"
      7 #include "base/files/file_path.h"
      8 #include "base/strings/string_util.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "base/win/registry.h"
     11 #include "chrome_frame/navigation_constraints.h"
     12 #include "chrome_frame/registry_list_preferences_holder.h"
     13 #include "chrome_frame/test/chrome_frame_test_utils.h"
     14 #include "chrome_frame/utils.h"
     15 
     16 #include "testing/gmock/include/gmock/gmock.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 using base::win::RegKey;
     20 
     21 const wchar_t kChannelName[] = L"-dev";
     22 const wchar_t kSuffix[] = L"-fix";
     23 
     24 // Registry override in the UtilsTest will cause shell APIs to fail
     25 // So separate this test from the rest
     26 TEST(SimpleUtilTests, GetTempInternetFiles) {
     27   base::FilePath path = GetIETemporaryFilesFolder();
     28   EXPECT_FALSE(path.empty());
     29 }
     30 
     31 class UtilTests : public testing::Test {
     32  protected:
     33   void SetUp() {
     34     DeleteAllSingletons();
     35   }
     36 
     37   // This is used to manage life cycle of PolicySettings singleton.
     38   // base::ShadowingAtExitManager at_exit_manager_;
     39   chrome_frame_test::ScopedVirtualizeHklmAndHkcu registry_virtualization_;
     40 };
     41 
     42 TEST_F(UtilTests, GetModuleVersionTest) {
     43   HMODULE mod = GetModuleHandle(L"kernel32.dll");
     44   EXPECT_NE(mod, static_cast<HMODULE>(NULL));
     45   wchar_t path[MAX_PATH] = {0};
     46   GetModuleFileName(mod, path, arraysize(path));
     47 
     48   // Use the method that goes to disk
     49   scoped_ptr<FileVersionInfo> base_info(
     50       FileVersionInfo::CreateFileVersionInfo(base::FilePath(path)));
     51   EXPECT_TRUE(base_info.get() != NULL);
     52 
     53   // Use the method that doesn't go to disk
     54   uint32 low = 0, high = 0;
     55   EXPECT_TRUE(GetModuleVersion(mod, &high, &low));
     56   EXPECT_NE(high, 0u);
     57   EXPECT_NE(low, 0u);
     58 
     59   // Make sure they give the same results.
     60   FileVersionInfoWin* base_info_win =
     61       static_cast<FileVersionInfoWin*>(base_info.get());
     62   VS_FIXEDFILEINFO* fixed_info = base_info_win->fixed_file_info();
     63   EXPECT_TRUE(fixed_info != NULL);
     64 
     65   EXPECT_EQ(fixed_info->dwFileVersionMS, static_cast<DWORD>(high));
     66   EXPECT_EQ(fixed_info->dwFileVersionLS, static_cast<DWORD>(low));
     67 }
     68 
     69 TEST_F(UtilTests, HaveSameOrigin) {
     70   struct OriginCompare {
     71     const char* a;
     72     const char* b;
     73     bool same_origin;
     74   } test_cases[] = {
     75     { "", "", true },
     76     { "*", "*", true },
     77     { "*", "+", false },
     78     { "http://www.google.com/", "http://www.google.com/", true },
     79     { "http://www.google.com", "http://www.google.com/", true },
     80     { "http://www.google.com:80/", "http://www.google.com/", true },
     81     { "http://www.google.com:8080/", "http://www.google.com/", false },
     82     { "https://www.google.com/", "http://www.google.com/", false },
     83     { "http://docs.google.com/", "http://www.google.com/", false },
     84     { "https://www.google.com/", "https://www.google.com:443/", true },
     85     { "https://www.google.com/", "https://www.google.com:443", true },
     86   };
     87 
     88   for (int i = 0; i < arraysize(test_cases); ++i) {
     89     const OriginCompare& test = test_cases[i];
     90     EXPECT_EQ(test.same_origin, HaveSameOrigin(test.a, test.b));
     91   }
     92 }
     93 
     94 TEST_F(UtilTests, IsValidUrlScheme) {
     95   struct Cases {
     96     const wchar_t* url;
     97     bool is_privileged;
     98     bool expected;
     99   } test_cases[] = {
    100     // non-privileged test cases
    101     { L"http://www.google.ca", false, true },
    102     { L"https://www.google.ca", false, true },
    103     { L"about:config", false, true },
    104     { L"view-source:http://www.google.ca", false, true },
    105     { L"chrome-extension://aaaaaaaaaaaaaaaaaaa/monkey.html", false, false },
    106     { L"ftp://www.google.ca", false, false },
    107     { L"file://www.google.ca", false, false },
    108     { L"file://C:\boot.ini", false, false },
    109 
    110     // privileged test cases
    111     { L"http://www.google.ca", true, true },
    112     { L"https://www.google.ca", true, true },
    113     { L"about:config", true, true },
    114     { L"view-source:http://www.google.ca", true, true },
    115     { L"chrome-extension://aaaaaaaaaaaaaaaaaaa/monkey.html", true, true },
    116     { L"ftp://www.google.ca", true, false },
    117     { L"file://www.google.ca", true, false },
    118     { L"file://C:\boot.ini", true, false },
    119   };
    120 
    121   for (int i = 0; i < arraysize(test_cases); ++i) {
    122     const Cases& test = test_cases[i];
    123     EXPECT_EQ(test.expected, IsValidUrlScheme(GURL(test.url),
    124                                               test.is_privileged));
    125   }
    126 }
    127 
    128 TEST_F(UtilTests, GuidToString) {
    129   // {3C5E2125-35BA-48df-A841-5F669B9D69FC}
    130   const GUID test_guid = { 0x3c5e2125, 0x35ba, 0x48df,
    131       { 0xa8, 0x41, 0x5f, 0x66, 0x9b, 0x9d, 0x69, 0xfc } };
    132 
    133   wchar_t compare[64] = {0};
    134   ::StringFromGUID2(test_guid, compare, arraysize(compare));
    135 
    136   std::wstring str_guid(GuidToString(test_guid));
    137   EXPECT_EQ(0, str_guid.compare(compare));
    138   EXPECT_EQ(static_cast<size_t>(lstrlenW(compare)), str_guid.length());
    139 }
    140 
    141 TEST_F(UtilTests, ParseAttachTabUrlTest) {
    142   ChromeFrameUrl cf_url;
    143 
    144   static const std::string kProfileName("iexplore");
    145 
    146   EXPECT_TRUE(cf_url.Parse(
    147       L"http://f/?attach_external_tab&10&1&2&3&123&321&iexplore"));
    148 
    149   EXPECT_TRUE(cf_url.attach_to_external_tab());
    150   EXPECT_FALSE(cf_url.is_chrome_protocol());
    151   EXPECT_EQ(10, cf_url.cookie());
    152   EXPECT_EQ(1, cf_url.disposition());
    153   EXPECT_EQ(gfx::Rect(2, 3, 123, 321), cf_url.dimensions());
    154   EXPECT_EQ(kProfileName, cf_url.profile_name());
    155 
    156   EXPECT_TRUE(cf_url.Parse(
    157       L"http://www.foobar.com?&10&1&2&3&123&321&iexplore"));
    158   EXPECT_FALSE(cf_url.attach_to_external_tab());
    159   EXPECT_FALSE(cf_url.is_chrome_protocol());
    160   EXPECT_EQ(0, cf_url.cookie());
    161   EXPECT_EQ(0, cf_url.disposition());
    162   EXPECT_EQ(gfx::Rect(0, 0, 0, 0), cf_url.dimensions());
    163   EXPECT_TRUE(cf_url.profile_name().empty());
    164 
    165   EXPECT_FALSE(cf_url.Parse(L"attach_external_tab&10&1"));
    166   EXPECT_FALSE(cf_url.attach_to_external_tab());
    167   EXPECT_FALSE(cf_url.is_chrome_protocol());
    168   EXPECT_EQ(0, cf_url.cookie());
    169   EXPECT_EQ(0, cf_url.disposition());
    170   EXPECT_EQ(gfx::Rect(0, 0, 0, 0), cf_url.dimensions());
    171   EXPECT_TRUE(cf_url.profile_name().empty());
    172 
    173   EXPECT_TRUE(cf_url.Parse(
    174       L"gcf:http://f/?attach_tab&10&1&2&3&123&321&iexplore"));
    175   EXPECT_FALSE(cf_url.attach_to_external_tab());
    176   EXPECT_TRUE(cf_url.is_chrome_protocol());
    177   EXPECT_EQ(0, cf_url.cookie());
    178   EXPECT_EQ(0, cf_url.disposition());
    179   EXPECT_EQ(gfx::Rect(0, 0, 0, 0), cf_url.dimensions());
    180   EXPECT_TRUE(cf_url.profile_name().empty());
    181 
    182   EXPECT_TRUE(cf_url.Parse(L"gcf:http://google.com"));
    183   EXPECT_FALSE(cf_url.attach_to_external_tab());
    184   EXPECT_TRUE(cf_url.is_chrome_protocol());
    185   EXPECT_EQ(0, cf_url.cookie());
    186   EXPECT_EQ(0, cf_url.disposition());
    187   EXPECT_EQ(gfx::Rect(0, 0, 0, 0), cf_url.dimensions());
    188   EXPECT_EQ(cf_url.gurl(), GURL("http://google.com"));
    189   EXPECT_TRUE(cf_url.profile_name().empty());
    190 }
    191 
    192 // Mock for the IInternetSecurityManager interface
    193 class MockIInternetSecurityManager : public IInternetSecurityManager {
    194  public:
    195   MOCK_METHOD2_WITH_CALLTYPE(__stdcall, QueryInterface,
    196       HRESULT(REFIID iid, void** object));
    197 
    198   MOCK_METHOD0_WITH_CALLTYPE(__stdcall, AddRef, ULONG());
    199   MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Release, ULONG());
    200 
    201   MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetSecuritySite,
    202       HRESULT(IInternetSecurityMgrSite* site));
    203   MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetSecuritySite,
    204       HRESULT(IInternetSecurityMgrSite** site));
    205   MOCK_METHOD3_WITH_CALLTYPE(__stdcall, MapUrlToZone,
    206       HRESULT(LPCWSTR url, DWORD* zone, DWORD flags));
    207   MOCK_METHOD4_WITH_CALLTYPE(__stdcall, GetSecurityId,
    208       HRESULT(LPCWSTR url, BYTE* security_id, DWORD* security_size,
    209               DWORD_PTR reserved));
    210   MOCK_METHOD8_WITH_CALLTYPE(__stdcall, ProcessUrlAction,
    211       HRESULT(LPCWSTR url, DWORD action, BYTE* policy, DWORD cb_policy,
    212               BYTE* context, DWORD context_size, DWORD flags,
    213               DWORD reserved));
    214   MOCK_METHOD7_WITH_CALLTYPE(__stdcall, QueryCustomPolicy,
    215       HRESULT(LPCWSTR url, REFGUID guid, BYTE** policy, DWORD* cb_policy,
    216               BYTE* context, DWORD cb_context, DWORD reserved));
    217   MOCK_METHOD3_WITH_CALLTYPE(__stdcall, SetZoneMapping,
    218       HRESULT(DWORD zone, LPCWSTR pattern, DWORD flags));
    219   MOCK_METHOD3_WITH_CALLTYPE(__stdcall, GetZoneMappings,
    220       HRESULT(DWORD zone, IEnumString** enum_string, DWORD flags));
    221 };
    222 
    223 // This class provides a partial mock for the NavigationConstraints
    224 // interface by providing specialized zone overrides.
    225 class MockNavigationConstraintsZoneOverride
    226     : public NavigationConstraintsImpl {
    227  public:
    228   MOCK_METHOD1(IsZoneAllowed, bool(const GURL&url));
    229 };
    230 
    231 // Mock NavigationConstraints
    232 class MockNavigationConstraints : public NavigationConstraints {
    233  public:
    234   MOCK_METHOD0(AllowUnsafeUrls, bool());
    235   MOCK_METHOD1(IsSchemeAllowed, bool(const GURL& url));
    236   MOCK_METHOD1(IsZoneAllowed, bool(const GURL& url));
    237 };
    238 
    239 // Matcher which returns true if the URL passed in starts with the prefix
    240 // specified.
    241 MATCHER_P(UrlPathStartsWith, url_prefix, "url starts with prefix") {
    242   return StartsWith(UTF8ToWide(arg.spec()), url_prefix, false);
    243 }
    244 
    245 ACTION_P3(HandleZone, mock, url_prefix, zone) {
    246   if (StartsWith(UTF8ToWide(arg0.spec()), url_prefix, false))
    247     return zone != URLZONE_UNTRUSTED;
    248   return false;
    249 }
    250 
    251 TEST_F(UtilTests, CanNavigateTest) {
    252   MockNavigationConstraintsZoneOverride mock;
    253 
    254   struct Zones {
    255     const wchar_t* url_prefix;
    256     URLZONE zone;
    257   } test_zones[] = {
    258     { L"http://blah", URLZONE_INTERNET },
    259     { L"http://untrusted", URLZONE_UNTRUSTED },
    260     { L"about:", URLZONE_TRUSTED },
    261     { L"view-source:", URLZONE_TRUSTED },
    262     { L"chrome-extension:", URLZONE_TRUSTED },
    263     { L"data:", URLZONE_INTERNET },
    264     { L"ftp:", URLZONE_UNTRUSTED },
    265     { L"file:", URLZONE_LOCAL_MACHINE },
    266     { L"sip:", URLZONE_UNTRUSTED },
    267   };
    268 
    269   for (int i = 0; i < arraysize(test_zones); ++i) {
    270     const Zones& zone = test_zones[i];
    271     EXPECT_CALL(mock, IsZoneAllowed(UrlPathStartsWith(zone.url_prefix)))
    272         .WillRepeatedly(testing::Return(zone.zone != URLZONE_UNTRUSTED));
    273   }
    274 
    275   struct Cases {
    276     const char* url;
    277     bool default_expected;
    278     bool unsafe_expected;
    279     bool is_privileged;
    280   } test_cases[] = {
    281     // Invalid URL
    282     { "          ", false, false, false },
    283     { "foo bar", false, false, false },
    284 
    285     // non-privileged test cases
    286     { "http://blah/?attach_external_tab&10&1&0&0&100&100&iexplore", true,
    287        true, false },
    288     { "http://untrusted/bar.html", false, true, false },
    289     { "http://blah/?attach_external_tab&10&1&0&0&100&100&iexplore", true,
    290        true, false },
    291     { "view-source:http://www.google.ca", true, true, false },
    292     { "view-source:javascript:alert('foo');", false, true, false },
    293     { "about:blank", true, true, false },
    294     { "About:Version", true, true, false },
    295     { "about:config", false, true, false },
    296     { "chrome-extension://aaaaaaaaaaaaaaaaaaa/monkey.html", false, true,
    297        false },
    298     { "ftp://www.google.ca", false, true, false },
    299     { "file://www.google.ca", false, true, false },
    300     { "file://C:\boot.ini", false, true, false },
    301     { "SIP:someone (at) 10.1.2.3", false, true, false },
    302 
    303     // privileged test cases
    304     { "chrome-extension://aaaaaaaaaaaaaaaaaaa/monkey.html", true, true,
    305        true },
    306     { "data://aaaaaaaaaaaaaaaaaaa/monkey.html", true, true, true },
    307   };
    308 
    309   for (int i = 0; i < arraysize(test_cases); ++i) {
    310     const Cases& test = test_cases[i];
    311     mock.set_is_privileged(test.is_privileged);
    312     bool actual = CanNavigate(GURL(test.url), &mock);
    313     EXPECT_EQ(test.default_expected, actual) << "Failure url: " << test.url;
    314   }
    315 }
    316 
    317 TEST_F(UtilTests, CanNavigateTestDenyAll) {
    318   MockNavigationConstraints mock;
    319 
    320   EXPECT_CALL(mock, IsZoneAllowed(testing::_))
    321       .Times(testing::AnyNumber())
    322       .WillRepeatedly(testing::Return(false));
    323 
    324   EXPECT_CALL(mock, IsSchemeAllowed(testing::_))
    325       .Times(testing::AnyNumber())
    326       .WillRepeatedly(testing::Return(false));
    327 
    328   EXPECT_CALL(mock, AllowUnsafeUrls())
    329       .Times(testing::AnyNumber())
    330       .WillRepeatedly(testing::Return(false));
    331 
    332   char *urls[] = {
    333     { "          "},
    334     { "foo bar"},
    335     // non-privileged test cases
    336     { "http://blah/?attach_external_tab&10&1&0&0&100&100&iexplore"},
    337     { "http://untrusted/bar.html"},
    338     { "http://blah/?attach_external_tab&10&1&0&0&100&100&iexplore"},
    339     { "view-source:http://www.google.ca"},
    340     { "view-source:javascript:alert('foo');"},
    341     { "about:blank"},
    342     { "About:Version"},
    343     { "about:config"},
    344     { "chrome-extension://aaaaaaaaaaaaaaaaaaa/monkey.html"},
    345     { "ftp://www.google.ca"},
    346     { "file://www.google.ca"},
    347     { "file://C:\boot.ini"},
    348     { "SIP:someone (at) 10.1.2.3"},
    349   };
    350 
    351   for (int i = 0; i < arraysize(urls); ++i) {
    352     EXPECT_FALSE(CanNavigate(GURL(urls[i]), &mock));
    353   }
    354 }
    355 
    356 TEST_F(UtilTests, CanNavigateTestAllowAll) {
    357   MockNavigationConstraints mock;
    358 
    359   EXPECT_CALL(mock, AllowUnsafeUrls())
    360       .Times(testing::AnyNumber())
    361       .WillRepeatedly(testing::Return(false));
    362 
    363   EXPECT_CALL(mock, IsSchemeAllowed(testing::_))
    364       .Times(testing::AnyNumber())
    365       .WillRepeatedly(testing::Return(true));
    366 
    367   EXPECT_CALL(mock, IsZoneAllowed(testing::_))
    368       .Times(testing::AnyNumber())
    369       .WillRepeatedly(testing::Return(true));
    370 
    371   char *urls[] = {
    372     // non-privileged test cases
    373     { "http://blah/?attach_external_tab&10&1&0&0&100&100&iexplore"},
    374     { "http://untrusted/bar.html"},
    375     { "http://blah/?attach_external_tab&10&1&0&0&100&100&iexplore"},
    376     { "view-source:http://www.google.ca"},
    377     { "view-source:javascript:alert('foo');"},
    378     { "about:blank"},
    379     { "About:Version"},
    380     { "about:config"},
    381     { "chrome-extension://aaaaaaaaaaaaaaaaaaa/monkey.html"},
    382     { "ftp://www.google.ca"},
    383     { "file://www.google.ca"},
    384     { "file://C:\boot.ini"},
    385     { "SIP:someone (at) 10.1.2.3"},
    386     { "gcf:about:cache"},
    387     { "gcf:about:plugins"},
    388   };
    389 
    390   for (int i = 0; i < arraysize(urls); ++i) {
    391     EXPECT_TRUE(CanNavigate(GURL(urls[i]), &mock));
    392   }
    393 }
    394 
    395 TEST_F(UtilTests, CanNavigateTestAllowAllUnsafeUrls) {
    396   MockNavigationConstraints mock;
    397 
    398   EXPECT_CALL(mock, AllowUnsafeUrls())
    399       .Times(testing::AnyNumber())
    400       .WillRepeatedly(testing::Return(true));
    401 
    402   char *urls[] = {
    403     {"gcf:about:cache"},
    404     {"gcf:http://www.google.com"},
    405     {"view-source:javascript:alert('foo');"},
    406     {"http://www.google.com"},
    407   };
    408 
    409   for (int i = 0; i < arraysize(urls); ++i) {
    410     EXPECT_TRUE(CanNavigate(GURL(urls[i]), &mock));
    411   }
    412 }
    413 
    414 TEST_F(UtilTests, IsDefaultRendererTest) {
    415   RegKey config_key(HKEY_CURRENT_USER, kChromeFrameConfigKey, KEY_ALL_ACCESS);
    416   EXPECT_TRUE(config_key.Valid());
    417 
    418   DWORD saved_default_renderer = 0;  // NOLINT
    419   config_key.ReadValueDW(kEnableGCFRendererByDefault, &saved_default_renderer);
    420 
    421   config_key.DeleteValue(kEnableGCFRendererByDefault);
    422   EXPECT_FALSE(IsGcfDefaultRenderer());
    423 
    424   config_key.WriteValue(kEnableGCFRendererByDefault, static_cast<DWORD>(0));
    425   EXPECT_FALSE(IsGcfDefaultRenderer());
    426 
    427   config_key.WriteValue(kEnableGCFRendererByDefault, static_cast<DWORD>(1));
    428   EXPECT_TRUE(IsGcfDefaultRenderer());
    429 
    430   config_key.WriteValue(kEnableGCFRendererByDefault, saved_default_renderer);
    431 }
    432 
    433 TEST_F(UtilTests, RendererTypeForUrlTest) {
    434   // Open all the keys we need.
    435   RegKey config_key(HKEY_CURRENT_USER, kChromeFrameConfigKey, KEY_ALL_ACCESS);
    436   EXPECT_TRUE(config_key.Valid());
    437   RegKey opt_for_gcf(config_key.Handle(), kRenderInGCFUrlList, KEY_ALL_ACCESS);
    438   EXPECT_TRUE(opt_for_gcf.Valid());
    439   RegKey opt_for_host(config_key.Handle(), kRenderInHostUrlList,
    440                       KEY_ALL_ACCESS);
    441   EXPECT_TRUE(opt_for_host.Valid());
    442   if (!config_key.Valid() || !opt_for_gcf.Valid() || !opt_for_host.Valid())
    443     return;
    444 
    445   const wchar_t kTestFilter[] = L"*.testing.chromium.org";
    446   const wchar_t kTestUrl[] = L"www.testing.chromium.org";
    447 
    448   // Save the current state of the registry.
    449   DWORD saved_default_renderer = 0;
    450   config_key.ReadValueDW(kEnableGCFRendererByDefault, &saved_default_renderer);
    451 
    452   // We need to manually reset the holder between checks.
    453   // TODO(robertshield): Remove this when the RegistryWatcher is wired up.
    454   RegistryListPreferencesHolder& renderer_type_preferences_holder =
    455       GetRendererTypePreferencesHolderForTesting();
    456 
    457   // Make sure the host is the default renderer.
    458   config_key.WriteValue(kEnableGCFRendererByDefault, static_cast<DWORD>(0));
    459   EXPECT_FALSE(IsGcfDefaultRenderer());
    460 
    461   opt_for_gcf.DeleteValue(kTestFilter);  // Just in case this exists
    462   EXPECT_EQ(RENDERER_TYPE_UNDETERMINED, RendererTypeForUrl(kTestUrl));
    463   opt_for_gcf.WriteValue(kTestFilter, L"");
    464   renderer_type_preferences_holder.ResetForTesting();
    465   EXPECT_EQ(RENDERER_TYPE_CHROME_OPT_IN_URL, RendererTypeForUrl(kTestUrl));
    466 
    467   // Now set GCF as the default renderer.
    468   config_key.WriteValue(kEnableGCFRendererByDefault, static_cast<DWORD>(1));
    469   EXPECT_TRUE(IsGcfDefaultRenderer());
    470 
    471   opt_for_host.DeleteValue(kTestFilter);  // Just in case this exists
    472   renderer_type_preferences_holder.ResetForTesting();
    473   EXPECT_EQ(RENDERER_TYPE_CHROME_DEFAULT_RENDERER,
    474             RendererTypeForUrl(kTestUrl));
    475   opt_for_host.WriteValue(kTestFilter, L"");
    476   renderer_type_preferences_holder.ResetForTesting();
    477   EXPECT_EQ(RENDERER_TYPE_UNDETERMINED, RendererTypeForUrl(kTestUrl));
    478 
    479   // Cleanup.
    480   opt_for_gcf.DeleteValue(kTestFilter);
    481   opt_for_host.DeleteValue(kTestFilter);
    482   config_key.WriteValue(kEnableGCFRendererByDefault, saved_default_renderer);
    483   renderer_type_preferences_holder.ResetForTesting();
    484   RendererTypeForUrl(L"");
    485 }
    486 
    487 TEST_F(UtilTests, XUaCompatibleDirectiveTest) {
    488   int all_versions[] = {0, 1, 2, 5, 6, 7, 8, 9, 10, 11, 99, 100, 101, 1000};
    489 
    490   struct Cases {
    491     const char* header_value;
    492     int max_version;
    493   } test_cases[] = {
    494     // Negative cases
    495     { "", -1 },
    496     { "chrome=", -1 },
    497     { "chrome", -1 },
    498     { "chrome=X", -1 },
    499     { "chrome=IE", -1 },
    500     { "chrome=IE-7", -1 },
    501     { "chrome=IE+7", -1 },
    502     { "chrome=IE 7", -1 },
    503     { "chrome=IE7.0", -1 },
    504     { "chrome=FF7", -1 },
    505     { "chrome=IE7+", -1 },
    506     { "chrome=IE99999999999999999999", -1 },
    507     { "chrome=IE0", -1 },
    508     // Always on
    509     { "chrome=1", INT_MAX },
    510     // Basic positive cases
    511     { "chrome=IE1", 1 },
    512     { "CHROME=IE6", 6 },
    513     { "Chrome=IE10", 10 },
    514     { "ChRoMe=IE100", 100 },
    515     // Positive formatting variations
    516     { "  chrome=IE6  ", 6 },
    517     { "  chrome=IE6;  ", 6 },
    518     { "  chrome=IE6; IE=8 ", 6 },
    519     { "  IE=8;chrome=IE6;", 6 },
    520     { "  IE=8;chrome=IE6;", 6 },
    521     { "  IE=8 ; chrome = IE6 ;", 6 },
    522     // Ignore unrecognized values
    523     { "  IE=8 ; chrome = IE7.1; chrome = IE6;", 6 },
    524     // First valid wins
    525     { "  IE=8 ; chrome = IE6; chrome = IE8;", 6 },
    526     // Comma delimiter
    527     { "  IE=8,chrome=IE6;", -1 },
    528     { "  IE=8,chrome=IE6", 6 },
    529     { "  IE=8,chrome=IE6, Something=Else;Why;Not", 6 },
    530     { "  IE=8,chrome=1,Something=Else", INT_MAX },
    531     { "  IE=8(a;b;c),chrome=IE7,Something=Else", 7 }
    532   };
    533 
    534   for (int case_index = 0; case_index < arraysize(test_cases); ++case_index) {
    535     const Cases& test = test_cases[case_index];
    536 
    537     // Check that all versions <= max_version are matched
    538     for (size_t version_index = 0;
    539          version_index < arraysize(all_versions);
    540          ++version_index) {
    541       bool expect_match = (all_versions[version_index] <= test.max_version);
    542 
    543       ASSERT_EQ(expect_match,
    544                 CheckXUaCompatibleDirective(test.header_value,
    545                                             all_versions[version_index]))
    546           << "Expect '" << test.header_value << "' to "
    547           << (expect_match ? "match" : "not match") << " IE major version "
    548           << all_versions[version_index];
    549     }
    550   }
    551 }
    552