| Left: | ||
| Right: |
| LEFT | RIGHT |
|---|---|
| 1 #!/bin/env python | 1 #!/bin/env python |
| 2 # This file is part of Adblock Plus <https://adblockplus.org/>, | 2 # This file is part of Adblock Plus <https://adblockplus.org/>, |
| 3 # Copyright (C) 2017-present eyeo GmbH | 3 # Copyright (C) 2017-present eyeo GmbH |
| 4 # | 4 # |
| 5 # Adblock Plus is free software: you can redistribute it and/or modify | 5 # Adblock Plus is free software: you can redistribute it and/or modify |
| 6 # it under the terms of the GNU General Public License version 3 as | 6 # it under the terms of the GNU General Public License version 3 as |
| 7 # published by the Free Software Foundation. | 7 # published by the Free Software Foundation. |
| 8 # | 8 # |
| 9 # Adblock Plus is distributed in the hope that it will be useful, | 9 # Adblock Plus is distributed in the hope that it will be useful, |
| 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 75 | 75 |
| 76 def print_diff(one, two): | 76 def print_diff(one, two): |
| 77 """Print unified diff between two files.""" | 77 """Print unified diff between two files.""" |
| 78 for line in difflib.unified_diff(read_file(one), read_file(two), | 78 for line in difflib.unified_diff(read_file(one), read_file(two), |
| 79 fromfile=one, tofile=two): | 79 fromfile=one, tofile=two): |
| 80 print(line) | 80 print(line) |
| 81 | 81 |
| 82 | 82 |
| 83 def compare_dirs(one, two, ignore=[]): | 83 def compare_dirs(one, two, ignore=[]): |
| 84 """Compare two directories, return True if same, False if not.""" | 84 """Compare two directories, return True if same, False if not.""" |
| 85 | |
| 86 def recursive_compare(c): | 85 def recursive_compare(c): |
| 87 if c.left_only: | 86 if c.left_only: |
| 88 print('The following file(s)/dir(s) are only in base', c.left) | 87 print('The following file(s)/dir(s) are only in base', c.left) |
| 89 for f in c.left_only: | 88 for f in c.left_only: |
| 90 print('-', f) | 89 print('-', f) |
| 91 return False | 90 return False |
| 92 if c.right_only: | 91 if c.right_only: |
| 93 print('The following file(s)/dir(s) are only in test', c.right) | 92 print('The following file(s)/dir(s) are only in test', c.right) |
| 94 for f in c.right_only: | 93 for f in c.right_only: |
| 95 print('-', f) | 94 print('-', f) |
| 96 return False | 95 return False |
| 97 if c.diff_files: | 96 if c.diff_files: |
| 98 print('The following file(s) are different between', c.left, | 97 print('The following file(s) are different between', c.left, |
| 99 'and', c.right) | 98 'and', c.right) |
| 100 for f in c.diff_files: | 99 for f in c.diff_files: |
| 101 print('-', f) | 100 print('-', f) |
| 102 base = os.path.join(c.left, f) | 101 base = os.path.join(c.left, f) |
| 103 test = os.path.join(c.right, f) | 102 test = os.path.join(c.right, f) |
| 104 print_diff(base, test) | 103 print_diff(base, test) |
| 105 return False | 104 return False |
| 106 return all(recursive_compare(sub) for sub in c.subdirs.values()) | 105 return all(recursive_compare(sub) for sub in c.subdirs.values()) |
| 107 | 106 |
| 108 print('Comparing', one, 'and', two) | 107 print('Comparing', one, 'and', two) |
| 109 comparator = filecmp.dircmp(one, two, ignore=ignore) | 108 comparator = filecmp.dircmp(one, two, ignore=ignore) |
| 110 return recursive_compare(comparator) | 109 return recursive_compare(comparator) |
| 111 | 110 |
| 112 | 111 |
| 113 class Tester(object): | 112 class Tester(object): |
| 114 """Test runner.""" | 113 """Test runner. |
| 115 | 114 |
| 116 def __init__(self, config): | 115 Generates one or more websites with two different versions of CMS and |
| 117 self.__dict__.update(config.__dict__) | 116 compares the results. |
| 117 """ | |
| 118 | |
| 119 def __init__(self, website_paths, cms_repo, dest, ignore=[], | |
| 120 python=sys.executable, remove_old=False, | |
| 121 base_rev='master', test_rev=WORKING_COPY): | |
| 122 """Create test runner. | |
| 123 | |
| 124 Parameters | |
| 125 ---------- | |
| 126 website_paths : list of strings | |
| 127 Paths of the website source folders that are used for comparisons. | |
| 128 cms_repo : str | |
| 129 Path to CMS repository. | |
| 130 dest : str | |
| 131 Directory into which CMS outputs will be placed. | |
| 132 ignore : list of strings | |
| 133 List of file names to ignore in output comparision. | |
| 134 python : str | |
| 135 Path to the python interpeter. | |
| 136 remove_old : bool | |
| 137 Remove results of earlier runs of this script? Setting this to | |
| 138 `True` will make things slower but will ensure correctness. | |
| 139 base_rev : str | |
| 140 Revision of CMS to use as a baseline. | |
| 141 test_rev : str | |
| 142 Revision of CMS to use for testing. | |
| 143 | |
| 144 """ | |
| 145 self.website_paths = website_paths | |
| 146 self.cms_repo = cms_repo | |
| 147 self.dest = dest | |
| 148 self.ignore = ignore | |
| 149 self.python = python | |
| 150 self.remove_old = remove_old | |
| 151 self.base_rev = base_rev | |
| 152 self.test_rev = test_rev | |
| 118 | 153 |
| 119 def clone_cms(self): | 154 def clone_cms(self): |
| 120 """Clone CMS repository for to use for tests.""" | 155 """Clone CMS repository for to use for tests.""" |
| 121 self.cms_clone = os.path.join(self.dest, 'cms-cmp.cms-clone') | 156 self.cms_clone = os.path.join(self.dest, 'cms-cmp.cms-clone') |
| 122 print('Cloning CMS to', self.cms_clone) | 157 print('Cloning CMS to', self.cms_clone) |
| 123 if os.path.exists(self.cms_clone): | 158 if os.path.exists(self.cms_clone): |
| 124 shutil.rmtree(self.cms_clone) | 159 shutil.rmtree(self.cms_clone) |
| 125 hg('clone', self.cms_repo, self.cms_clone) | 160 hg('clone', self.cms_repo, self.cms_clone) |
| 126 | 161 |
| 127 def cms_checkout(self, rev): | 162 def cms_checkout(self, rev): |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 150 else: | 185 else: |
| 151 print(dst, 'exists, assuming it was generated earlier') | 186 print(dst, 'exists, assuming it was generated earlier') |
| 152 return dst | 187 return dst |
| 153 env = dict(os.environ) | 188 env = dict(os.environ) |
| 154 env['PYTHONPATH'] = cms_path | 189 env['PYTHONPATH'] = cms_path |
| 155 generate = os.path.join(cms_path, GENERATE_PATH) | 190 generate = os.path.join(cms_path, GENERATE_PATH) |
| 156 run_cmd(self.python, generate, website_path, dst, env=env) | 191 run_cmd(self.python, generate, website_path, dst, env=env) |
| 157 return dst | 192 return dst |
| 158 | 193 |
| 159 def run(self): | 194 def run(self): |
| 195 """Run the comparison.""" | |
| 160 if not os.path.exists(os.path.join(self.cms_repo, GENERATE_PATH)): | 196 if not os.path.exists(os.path.join(self.cms_repo, GENERATE_PATH)): |
| 161 sys.exit('No cms source found in ' + self.cms_repo) | 197 sys.exit('No cms source found in ' + self.cms_repo) |
| 162 print('Using CMS repository at', self.cms_repo) | 198 print('Using CMS repository at', self.cms_repo) |
| 163 self.clone_cms() | 199 self.clone_cms() |
| 164 for website_path in self.websites: | 200 for website_path in self.website_paths: |
| 165 base = self.generate(self.base_rev, website_path) | 201 base = self.generate(self.base_rev, website_path) |
| 166 test = self.generate(self.test_rev, website_path) | 202 test = self.generate(self.test_rev, website_path) |
| 167 if not compare_dirs(base, test, ignore=self.ignore): | 203 if not compare_dirs(base, test, ignore=self.ignore): |
| 168 print('Differences found for', website_path) | 204 print('Differences found for', website_path) |
| 169 sys.exit(1) | 205 sys.exit(1) |
| 170 | 206 |
| 171 | 207 |
| 172 def configure(): | 208 def configure(): |
|
tlucas
2017/11/24 10:57:46
Since configure()'s only purpose is to specify par
Vasily Kuznetsov
2017/11/28 16:53:37
I would like to keep `Tester` independent from par
tlucas
2017/12/14 10:30:42
ACK, your argumentation seems valid :)
| |
| 173 """Configure the script from arguments.""" | 209 """Configure the script from arguments.""" |
| 174 parser = argparse.ArgumentParser(description=__doc__) | 210 parser = argparse.ArgumentParser(description=__doc__) |
| 175 parser.add_argument('websites', metavar='WEBSITE', nargs='+', | 211 parser.add_argument('website_paths', metavar='WEBSITE', nargs='+', |
| 176 help='path of website source to use for comparison') | 212 help='path of website source to use for comparison') |
| 177 parser.add_argument('-b', '--base-rev', metavar='REVISION', | 213 parser.add_argument('-b', '--base-rev', metavar='REVISION', |
| 178 default='master', | 214 default='master', |
| 179 help='base revision of CMS to use as baseline') | 215 help='base revision of CMS to use as baseline') |
| 180 parser.add_argument('-c', '--cms-repo', metavar='CMS_REPO', | 216 parser.add_argument('-c', '--cms-repo', metavar='CMS_REPO', |
| 181 default=os.getcwd(), | 217 default=os.getcwd(), |
| 182 help='Location of CMS repository') | 218 help='Location of CMS repository') |
| 183 parser.add_argument('-d', '--dest', metavar='OUT_DIR', | 219 parser.add_argument('-d', '--dest', metavar='OUT_DIR', |
| 184 default=os.environ.get('TMPDIR', '/tmp'), | 220 default=os.environ.get('TMPDIR', '/tmp'), |
| 185 help='directory for storing CMS output') | 221 help='directory for storing CMS output') |
| 186 parser.add_argument('-i', '--ignore', metavar='FILENAME', action='append', | 222 parser.add_argument('-i', '--ignore', metavar='FILENAME', action='append', |
| 187 default=[], | 223 default=[], |
| 188 help='file names to ignore in output comparison') | 224 help='file names to ignore in output comparison') |
| 189 parser.add_argument('-p', '--python', metavar='PYTHON_EXE', | 225 parser.add_argument('-p', '--python', metavar='PYTHON_EXE', |
| 190 default=sys.executable, | 226 default=sys.executable, |
| 191 help='python interpreter to run CMS') | 227 help='python interpreter to run CMS') |
| 192 parser.add_argument('-r', '--remove-old', action='store_true', | 228 parser.add_argument('-r', '--remove-old', action='store_true', |
| 193 help='remove previously generated CMS outputs instead' | 229 help='remove previously generated CMS outputs instead' |
| 194 'of reusing them') | 230 'of reusing them') |
| 195 parser.add_argument('-t', '--test-rev', metavar='REVISION', | 231 parser.add_argument('-t', '--test-rev', metavar='REVISION', |
| 196 default=WORKING_COPY, | 232 default=WORKING_COPY, |
| 197 help='revision of CMS to use for testing (by default ' | 233 help='revision of CMS to use for testing (by default ' |
| 198 'currently checked out working copy including ' | 234 'currently checked out working copy including ' |
| 199 'uncommited changes will be used)') | 235 'uncommited changes will be used)') |
| 200 return parser.parse_args() | 236 return parser.parse_args() |
| 201 | 237 |
| 202 | 238 |
| 203 def main(): | 239 def main(): |
| 204 """Main entry point for the script.""" | 240 """Parse command line arguments and run the tests. |
| 241 | |
| 242 Main entry point for the script. | |
| 243 """ | |
| 205 config = configure() | 244 config = configure() |
| 206 tester = Tester(config) | 245 tester = Tester(**config.__dict__) |
| 207 tester.run() | 246 tester.run() |
| 208 | 247 |
| 209 | 248 |
| 210 if __name__ == '__main__': | 249 if __name__ == '__main__': |
| 211 main() | 250 main() |
| LEFT | RIGHT |