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 |