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