| #!/usr/bin/env python | 
 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | """Tests for checkdeps. | 
 | """ | 
 |  | 
 | import os | 
 | import unittest | 
 |  | 
 |  | 
 | import builddeps | 
 | import checkdeps | 
 | import results | 
 |  | 
 |  | 
 | class CheckDepsTest(unittest.TestCase): | 
 |  | 
 |   def setUp(self): | 
 |     self.deps_checker = checkdeps.DepsChecker( | 
 |         being_tested=True, | 
 |         base_directory=os.path.join(os.path.dirname(__file__), os.path.pardir)) | 
 |  | 
 |   def ImplTestRegularCheckDepsRun(self, ignore_temp_rules, skip_tests): | 
 |     self.deps_checker._ignore_temp_rules = ignore_temp_rules | 
 |     self.deps_checker._skip_tests = skip_tests | 
 |     self.deps_checker.CheckDirectory( | 
 |         os.path.join(self.deps_checker.base_directory, | 
 |                      'checkdeps/testdata')) | 
 |  | 
 |     problems = self.deps_checker.results_formatter.GetResults() | 
 |     if skip_tests: | 
 |       self.failUnlessEqual(4, len(problems)) | 
 |     else: | 
 |       self.failUnlessEqual(5, len(problems)) | 
 |  | 
 |     def VerifySubstringsInProblems(key_path, substrings_in_sequence): | 
 |       """Finds the problem in |problems| that contains |key_path|, | 
 |       then verifies that each of |substrings_in_sequence| occurs in | 
 |       that problem, in the order they appear in | 
 |       |substrings_in_sequence|. | 
 |       """ | 
 |       found = False | 
 |       key_path = os.path.normpath(key_path) | 
 |       for problem in problems: | 
 |         index = problem.find(key_path) | 
 |         if index != -1: | 
 |           for substring in substrings_in_sequence: | 
 |             index = problem.find(substring, index + 1) | 
 |             self.failUnless(index != -1, '%s in %s' % (substring, problem)) | 
 |           found = True | 
 |           break | 
 |       if not found: | 
 |         self.fail('Found no problem for file %s' % key_path) | 
 |  | 
 |     if ignore_temp_rules: | 
 |       VerifySubstringsInProblems('testdata/allowed/test.h', | 
 |                                  ['-checkdeps/testdata/disallowed', | 
 |                                   'temporarily_allowed.h', | 
 |                                   '-third_party/explicitly_disallowed', | 
 |                                   'Because of no rule applying']) | 
 |     else: | 
 |       VerifySubstringsInProblems('testdata/allowed/test.h', | 
 |                                  ['-checkdeps/testdata/disallowed', | 
 |                                   '-third_party/explicitly_disallowed', | 
 |                                   'Because of no rule applying']) | 
 |  | 
 |     VerifySubstringsInProblems('testdata/disallowed/test.h', | 
 |                                ['-third_party/explicitly_disallowed', | 
 |                                 'Because of no rule applying', | 
 |                                 'Because of no rule applying']) | 
 |     VerifySubstringsInProblems('disallowed/allowed/test.h', | 
 |                                ['-third_party/explicitly_disallowed', | 
 |                                 'Because of no rule applying', | 
 |                                 'Because of no rule applying']) | 
 |     VerifySubstringsInProblems('testdata/noparent/test.h', | 
 |                                ['allowed/bad.h', | 
 |                                 'Because of no rule applying']) | 
 |  | 
 |     if not skip_tests: | 
 |       VerifySubstringsInProblems('allowed/not_a_test.cc', | 
 |                                  ['-checkdeps/testdata/disallowed']) | 
 |  | 
 |   def testRegularCheckDepsRun(self): | 
 |     self.ImplTestRegularCheckDepsRun(False, False) | 
 |  | 
 |   def testRegularCheckDepsRunIgnoringTempRules(self): | 
 |     self.ImplTestRegularCheckDepsRun(True, False) | 
 |  | 
 |   def testRegularCheckDepsRunSkipTests(self): | 
 |     self.ImplTestRegularCheckDepsRun(False, True) | 
 |  | 
 |   def testRegularCheckDepsRunIgnoringTempRulesSkipTests(self): | 
 |     self.ImplTestRegularCheckDepsRun(True, True) | 
 |  | 
 |   def CountViolations(self, ignore_temp_rules): | 
 |     self.deps_checker._ignore_temp_rules = ignore_temp_rules | 
 |     self.deps_checker.results_formatter = results.CountViolationsFormatter() | 
 |     self.deps_checker.CheckDirectory( | 
 |         os.path.join(self.deps_checker.base_directory, | 
 |                      'checkdeps/testdata')) | 
 |     return self.deps_checker.results_formatter.GetResults() | 
 |  | 
 |   def testCountViolations(self): | 
 |     self.failUnlessEqual('11', self.CountViolations(False)) | 
 |  | 
 |   def testCountViolationsIgnoringTempRules(self): | 
 |     self.failUnlessEqual('12', self.CountViolations(True)) | 
 |  | 
 |   def testCountViolationsWithRelativePath(self): | 
 |     self.deps_checker.results_formatter = results.CountViolationsFormatter() | 
 |     self.deps_checker.CheckDirectory( | 
 |         os.path.join('checkdeps', 'testdata', 'allowed')) | 
 |     self.failUnlessEqual('4', self.deps_checker.results_formatter.GetResults()) | 
 |  | 
 |   def testTempRulesGenerator(self): | 
 |     self.deps_checker.results_formatter = results.TemporaryRulesFormatter() | 
 |     self.deps_checker.CheckDirectory( | 
 |         os.path.join(self.deps_checker.base_directory, | 
 |                      'checkdeps/testdata/allowed')) | 
 |     temp_rules = self.deps_checker.results_formatter.GetResults() | 
 |     expected = [u'  "!checkdeps/testdata/disallowed/bad.h",', | 
 |                 u'  "!checkdeps/testdata/disallowed/teststuff/bad.h",', | 
 |                 u'  "!third_party/explicitly_disallowed/bad.h",', | 
 |                 u'  "!third_party/no_rule/bad.h",'] | 
 |     self.failUnlessEqual(expected, temp_rules) | 
 |  | 
 |   def testBadBaseDirectoryNotCheckoutRoot(self): | 
 |     # This assumes git. It's not a valid test if buildtools is fetched via svn. | 
 |     with self.assertRaises(builddeps.DepsBuilderError): | 
 |       checkdeps.DepsChecker(being_tested=True, | 
 |                             base_directory=os.path.dirname(__file__)) | 
 |  | 
 |   def testCheckAddedIncludesAllGood(self): | 
 |     problems = self.deps_checker.CheckAddedCppIncludes( | 
 |       [['checkdeps/testdata/allowed/test.cc', | 
 |         ['#include "checkdeps/testdata/allowed/good.h"', | 
 |          '#include "checkdeps/testdata/disallowed/allowed/good.h"'] | 
 |       ]]) | 
 |     self.failIf(problems) | 
 |  | 
 |   def testCheckAddedIncludesManyGarbageLines(self): | 
 |     garbage_lines = ["My name is Sam%d\n" % num for num in range(50)] | 
 |     problems = self.deps_checker.CheckAddedCppIncludes( | 
 |       [['checkdeps/testdata/allowed/test.cc', garbage_lines]]) | 
 |     self.failIf(problems) | 
 |  | 
 |   def testCheckAddedIncludesNoRule(self): | 
 |     problems = self.deps_checker.CheckAddedCppIncludes( | 
 |       [['checkdeps/testdata/allowed/test.cc', | 
 |         ['#include "no_rule_for_this/nogood.h"'] | 
 |       ]]) | 
 |     self.failUnless(problems) | 
 |  | 
 |   def testCheckAddedIncludesSkippedDirectory(self): | 
 |     problems = self.deps_checker.CheckAddedCppIncludes( | 
 |       [['checkdeps/testdata/disallowed/allowed/skipped/test.cc', | 
 |         ['#include "whatever/whocares.h"'] | 
 |       ]]) | 
 |     self.failIf(problems) | 
 |  | 
 |   def testCheckAddedIncludesTempAllowed(self): | 
 |     problems = self.deps_checker.CheckAddedCppIncludes( | 
 |       [['checkdeps/testdata/allowed/test.cc', | 
 |         ['#include "checkdeps/testdata/disallowed/temporarily_allowed.h"'] | 
 |       ]]) | 
 |     self.failUnless(problems) | 
 |  | 
 |   def testCopyIsDeep(self): | 
 |     # Regression test for a bug where we were making shallow copies of | 
 |     # Rules objects and therefore all Rules objects shared the same | 
 |     # dictionary for specific rules. | 
 |     # | 
 |     # The first pair should bring in a rule from testdata/allowed/DEPS | 
 |     # into that global dictionary that allows the | 
 |     # temp_allowed_for_tests.h file to be included in files ending | 
 |     # with _unittest.cc, and the second pair should completely fail | 
 |     # once the bug is fixed, but succeed (with a temporary allowance) | 
 |     # if the bug is in place. | 
 |     problems = self.deps_checker.CheckAddedCppIncludes( | 
 |       [['checkdeps/testdata/allowed/test.cc', | 
 |         ['#include "/checkdeps/testdata/disallowed/temporarily_allowed.h"'] | 
 |        ], | 
 |        ['checkdeps/testdata/disallowed/foo_unittest.cc', | 
 |         ['#include "checkdeps/testdata/bongo/temp_allowed_for_tests.h"'] | 
 |        ]]) | 
 |     # With the bug in place, there would be two problems reported, and | 
 |     # the second would be for foo_unittest.cc. | 
 |     self.failUnless(len(problems) == 1) | 
 |     self.failUnless(problems[0][0].endswith('/test.cc')) | 
 |  | 
 |   def testTraversalIsOrdered(self): | 
 |     dirs_traversed = [] | 
 |     for rules, filenames in self.deps_checker.GetAllRulesAndFiles(): | 
 |       self.failUnlessEqual(type(filenames), list) | 
 |       self.failUnlessEqual(filenames, sorted(filenames)) | 
 |       if filenames: | 
 |         dir_names = set(os.path.dirname(file) for file in filenames) | 
 |         self.failUnlessEqual(1, len(dir_names)) | 
 |         dirs_traversed.append(dir_names.pop()) | 
 |     self.failUnlessEqual(dirs_traversed, sorted(dirs_traversed)) | 
 |  | 
 |   def testCheckPartialImportsAreAllowed(self): | 
 |     problems = self.deps_checker.CheckAddedProtoImports( | 
 |       [['checkdeps/testdata/test.proto', | 
 |         ['import "no_rule_for_this/nogood.proto"'] | 
 |       ]]) | 
 |     self.failIf(problems) | 
 |  | 
 |   def testCheckAddedFullPathImportsAllowed(self): | 
 |     # NOTE: Base directory is buildtools. | 
 |     problems = self.deps_checker.CheckAddedProtoImports( | 
 |       [['checkdeps/testdata/test.proto', | 
 |         ['import "checkdeps/testdata/allowed/good.proto"', | 
 |          'import "checkdeps/testdata/disallowed/sub_folder/good.proto"'] | 
 |       ]]) | 
 |     self.failIf(problems) | 
 |  | 
 |   def testCheckAddedFullPathImportsDisallowed(self): | 
 |     problems = self.deps_checker.CheckAddedProtoImports( | 
 |       [['checkdeps/testdata/test.proto', | 
 |         ['import "checkdeps/testdata/disallowed/bad.proto"'] | 
 |       ]]) | 
 |     self.failUnless(problems) | 
 |  | 
 |   def testCheckAddedFullPathImportsManyGarbageLines(self): | 
 |     garbage_lines = ["My name is Sam%d\n" % num for num in range(50)] | 
 |     problems = self.deps_checker.CheckAddedProtoImports( | 
 |       [['checkdeps/testdata/test.proto', | 
 |         garbage_lines]]) | 
 |     self.failIf(problems) | 
 |  | 
 |   def testCheckAddedIncludesNoRuleFullPath(self): | 
 |     problems = self.deps_checker.CheckAddedProtoImports( | 
 |       [['checkdeps/testdata/test.proto', | 
 |         ['import "../tools/some.proto"'] | 
 |       ]]) | 
 |     self.failUnless(problems) | 
 |  | 
 | if __name__ == '__main__': | 
 |   unittest.main() |