1// Copyright (c) 2011 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/string_number_conversions.h"
6#include "base/string_util.h"
7#include "base/utf_string_conversions.h"
8#include "chrome/browser/enumerate_modules_model_win.h"
9#include "testing/gtest/include/gtest/gtest.h"
10
11typedef public testing::Test EnumerateModulesTest;
12
13// Set up some constants to use as default when creating the structs.
14static const ModuleEnumerator::ModuleType kType =
15    ModuleEnumerator::LOADED_MODULE;
16
17static const ModuleEnumerator::ModuleStatus kStatus =
18    ModuleEnumerator::NOT_MATCHED;
19
20static const ModuleEnumerator::RecommendedAction kAction =
21    ModuleEnumerator::NONE;
22
23// This is a list of test cases to normalize.
24static const struct NormalizationEntryList {
25  ModuleEnumerator::Module test_case;
26  ModuleEnumerator::Module expected;
27} kNormalizationTestCases[] = {
28  {
29    // Only path normalization needed.
30    {kType, kStatus, L"c:\\foo\\bar.dll", L"",        L"Prod", L"Desc", L"1.0",
31         L"Sig", kAction},
32    {kType, kStatus, L"c:\\foo\\",        L"bar.dll", L"Prod", L"Desc", L"1.0",
33         L"Sig", kAction},
34  }, {
35    // Lower case normalization.
36    {kType, kStatus, L"C:\\Foo\\Bar.dll", L"",        L"", L"", L"1.0",
37         L"", kAction},
38    {kType, kStatus, L"c:\\foo\\",        L"bar.dll", L"", L"", L"1.0",
39         L"", kAction},
40  }, {
41    // Version can include strings after the version number. Strip that away.
42    {kType, kStatus, L"c:\\foo.dll", L"",        L"", L"", L"1.0 asdf",
43         L"", kAction},
44    {kType, kStatus, L"c:\\",        L"foo.dll", L"", L"", L"1.0",
45         L"", kAction},
46  }, {
47    // Corner case: No path (not sure this will ever happen).
48    {kType, kStatus, L"bar.dll", L"",        L"", L"", L"", L"", kAction},
49    {kType, kStatus, L"",        L"bar.dll", L"", L"", L"", L"", kAction},
50  }, {
51    // Error case: Missing filename (not sure this will ever happen).
52    {kType, kStatus, L"", L"", L"", L"", L"1.0", L"", kAction},
53    {kType, kStatus, L"", L"", L"", L"", L"1.0", L"", kAction},
54  },
55};
56
57TEST_F(EnumerateModulesTest, NormalizeEntry) {
58  for (size_t i = 0; i < arraysize(kNormalizationTestCases); ++i) {
59    ModuleEnumerator::Module test = kNormalizationTestCases[i].test_case;
60    EXPECT_FALSE(test.normalized);
61    ModuleEnumerator::NormalizeModule(&test);
62    ModuleEnumerator::Module expected = kNormalizationTestCases[i].expected;
63
64    SCOPED_TRACE("Test case no: " + base::IntToString(i));
65    EXPECT_EQ(expected.type, test.type);
66    EXPECT_EQ(expected.status, test.status);
67    EXPECT_STREQ(expected.location.c_str(), test.location.c_str());
68    EXPECT_STREQ(expected.name.c_str(), test.name.c_str());
69    EXPECT_STREQ(expected.product_name.c_str(), test.product_name.c_str());
70    EXPECT_STREQ(expected.description.c_str(), test.description.c_str());
71    EXPECT_STREQ(expected.version.c_str(), test.version.c_str());
72    EXPECT_STREQ(expected.digital_signer.c_str(), test.digital_signer.c_str());
73    EXPECT_EQ(expected.recommended_action, test.recommended_action);
74    EXPECT_TRUE(test.normalized);
75  }
76}
77
78const ModuleEnumerator::Module kStandardModule =
79  { kType, kStatus, L"c:\\foo\\bar.dll", L"", L"Prod", L"Desc", L"1.0", L"Sig",
80    ModuleEnumerator::NONE };
81const ModuleEnumerator::Module kStandardModuleNoDescription =
82  { kType, kStatus, L"c:\\foo\\bar.dll", L"", L"Prod", L"", L"1.0", L"Sig",
83    ModuleEnumerator::NONE };
84const ModuleEnumerator::Module kStandardModuleNoSignature =
85  { kType, kStatus, L"c:\\foo\\bar.dll", L"", L"Prod", L"Desc", L"1.0", L"",
86    ModuleEnumerator::NONE };
87
88// Name, location, description and signature are compared by hashing.
89static const char kMatchName[] = "88e8c9e0";             // "bar.dll".
90static const char kNoMatchName[] = "barfoo.dll";
91static const char kMatchLocation[] = "e6ca7b1c";         // "c:\\foo\\".
92static const char kNoMatchLocation[] = "c:\\foobar\\";
93static const char kMatchDesc[] = "5c4419a6";             // "Desc".
94static const char kNoMatchDesc[] = "NoDesc";
95static const char kVersionHigh[] = "2.0";
96static const char kVersionLow[] = "0.5";
97static const char kMatchSignature[] = "7bfd87e1";        // "Sig".
98static const char kNoMatchSignature[] = "giS";
99static const char kEmpty[] = "";
100
101const struct MatchingEntryList {
102  ModuleEnumerator::ModuleStatus expected_result;
103  ModuleEnumerator::Module test_case;
104  ModuleEnumerator::BlacklistEntry blacklist;
105} kMatchineEntryList[] = {
106  // Each BlacklistEntry is:
107  // Filename, location, desc_or_signer, version from, version to, help_tip.
108
109  {  // Matches: Name (location doesn't match) => Not enough for a match.
110    ModuleEnumerator::NOT_MATCHED,
111    kStandardModule,
112    { kMatchName, kNoMatchLocation, kEmpty, kEmpty, kEmpty,
113      ModuleEnumerator::SEE_LINK }
114  }, {  // Matches: Name (location not given) => Suspected match.
115    ModuleEnumerator::SUSPECTED_BAD,
116    kStandardModule,
117    { kMatchName, kEmpty, kEmpty, kEmpty, kEmpty,
118      ModuleEnumerator::SEE_LINK }
119  }, {  // Matches: Name, not version (location not given) => Not a match.
120    ModuleEnumerator::NOT_MATCHED,
121    kStandardModule,
122    { kMatchName, kEmpty, kEmpty, kVersionHigh, kVersionHigh,
123      ModuleEnumerator::SEE_LINK }
124  }, {  // Matches: Name, location => Suspected match.
125    ModuleEnumerator::SUSPECTED_BAD,
126    kStandardModule,
127    { kMatchName, kMatchLocation, kEmpty, kEmpty, kEmpty,
128      ModuleEnumerator::SEE_LINK }
129  }, {  // Matches: Name, location, (description not given) => Confirmed match.
130    ModuleEnumerator::CONFIRMED_BAD,
131    kStandardModuleNoDescription,  // Note: No description.
132    { kMatchName, kMatchLocation, kEmpty, kEmpty, kEmpty,
133      ModuleEnumerator::SEE_LINK }
134  }, {  // Matches: Name, location, (signature not given) => Confirmed match.
135    ModuleEnumerator::CONFIRMED_BAD,
136    kStandardModuleNoSignature,  // Note: No signature.
137    { kMatchName, kMatchLocation, kEmpty, kEmpty, kEmpty,
138      ModuleEnumerator::SEE_LINK }
139  }, {  // Matches: Name, location (not version) => Not a match.
140    ModuleEnumerator::NOT_MATCHED,
141    kStandardModule,
142    { kMatchName, kMatchLocation, kEmpty, kVersionHigh, kVersionLow,
143      ModuleEnumerator::SEE_LINK }
144  }, {  // Matches: Name, location, signature => Confirmed match.
145    ModuleEnumerator::CONFIRMED_BAD,
146    kStandardModule,
147    { kMatchName, kMatchLocation, kMatchSignature, kEmpty, kEmpty,
148      ModuleEnumerator::SEE_LINK }
149  }, {  // Matches: Name, location, signature (not version) => No match.
150    ModuleEnumerator::NOT_MATCHED,
151    kStandardModule,
152    { kMatchName, kMatchLocation, kMatchSignature,
153      kVersionLow, kVersionLow, ModuleEnumerator::SEE_LINK }
154  }, {  // Matches: Name, location, description => Confirmed match.
155    ModuleEnumerator::CONFIRMED_BAD,
156    kStandardModule,
157    { kMatchName, kMatchLocation, kMatchDesc, kEmpty, kEmpty,
158      ModuleEnumerator::SEE_LINK }
159  }, {  // Matches: Name, location, description (not version) => No match.
160    ModuleEnumerator::NOT_MATCHED,
161    kStandardModule,
162    { kMatchName, kMatchLocation, kMatchDesc,
163      kVersionHigh, kVersionHigh, ModuleEnumerator::SEE_LINK }
164  }, {  // Matches: Name, location, signature, version => Confirmed match.
165    ModuleEnumerator::CONFIRMED_BAD,
166    kStandardModule,
167    { kMatchName, kMatchLocation, kMatchSignature,
168      kVersionLow, kVersionHigh, ModuleEnumerator::SEE_LINK }
169  }, {  // Matches: Name, location, signature, version (lower) => Confirmed.
170    ModuleEnumerator::CONFIRMED_BAD,
171    kStandardModule,
172    { kMatchName, kMatchLocation, kMatchSignature,
173      kVersionLow, kEmpty, ModuleEnumerator::SEE_LINK }
174  }, {  // Matches: Name, location, signature, version (upper) => Confirmed.
175    ModuleEnumerator::CONFIRMED_BAD,
176    kStandardModule,
177    { kMatchName, kMatchLocation, kMatchSignature,
178      kEmpty, kVersionHigh, ModuleEnumerator::SEE_LINK }
179  }, {  // Matches: Name, Location, Version lower is inclusive => Confirmed.
180    ModuleEnumerator::CONFIRMED_BAD,
181    kStandardModule,
182    { kMatchName, kMatchLocation, kMatchSignature,
183      "1.0", "2.0", ModuleEnumerator::SEE_LINK }
184  }, {  // Matches: Name, Location, Version higher is exclusive => No match.
185    ModuleEnumerator::NOT_MATCHED,
186    kStandardModule,
187    { kMatchName, kMatchLocation, kEmpty,
188      "0.0", "1.0", ModuleEnumerator::SEE_LINK }
189  }, {  // All empty fields doesn't produce a match.
190    ModuleEnumerator::NOT_MATCHED,
191    { kType, kStatus, L"", L"", L"", L"", L""},
192    { "a.dll", "", "", "", "", ModuleEnumerator::SEE_LINK }
193  },
194};
195
196TEST_F(EnumerateModulesTest, MatchFunction) {
197  for (size_t i = 0; i < arraysize(kMatchineEntryList); ++i) {
198    ModuleEnumerator::Module test = kMatchineEntryList[i].test_case;
199    ModuleEnumerator::NormalizeModule(&test);
200    ModuleEnumerator::BlacklistEntry blacklist =
201        kMatchineEntryList[i].blacklist;
202
203    SCOPED_TRACE("Test case no " + base::IntToString(i) +
204                 ": '" + UTF16ToASCII(test.name) + "'");
205    EXPECT_EQ(kMatchineEntryList[i].expected_result,
206              ModuleEnumerator::Match(test, blacklist));
207  }
208}
209
210const struct CollapsePathList {
211  string16 expected_result;
212  string16 test_case;
213} kCollapsePathList[] = {
214  // Negative testing (should not collapse this path).
215  { ASCIIToUTF16("c:\\a\\a.dll"), ASCIIToUTF16("c:\\a\\a.dll") },
216  // These two are to test that we select the maximum collapsed path.
217  { ASCIIToUTF16("%foo%\\a.dll"), ASCIIToUTF16("c:\\foo\\a.dll") },
218  { ASCIIToUTF16("%x%\\a.dll"), ASCIIToUTF16("c:\\foo\\bar\\a.dll") },
219};
220
221TEST_F(EnumerateModulesTest, CollapsePath) {
222  scoped_refptr<ModuleEnumerator> module_enumerator(new ModuleEnumerator(NULL));
223  module_enumerator->path_mapping_.clear();
224  module_enumerator->path_mapping_.push_back(
225      std::make_pair(L"c:\\foo\\", L"%foo%"));
226  module_enumerator->path_mapping_.push_back(
227      std::make_pair(L"c:\\foo\\bar\\", L"%x%"));
228
229  for (size_t i = 0; i < arraysize(kCollapsePathList); ++i) {
230    ModuleEnumerator::Module module;
231    module.location = kCollapsePathList[i].test_case;
232    module_enumerator->CollapsePath(&module);
233
234    SCOPED_TRACE("Test case no " + base::IntToString(i) +
235                 ": '" + UTF16ToASCII(kCollapsePathList[i].expected_result) +
236                 "'");
237    EXPECT_EQ(kCollapsePathList[i].expected_result, module.location);
238  }
239}
240