Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: cms-dev/cms_cmp.py

Issue 29588962: Issue 5934 - CMS testing automation (Closed)
Left Patch Set: Change the usage a bit, add some useful options, add a couple of tests Created Oct. 27, 2017, 5:38 p.m.
Right Patch Set: Improve documentation and structure following Tristan's comments Created Nov. 28, 2017, 4:11 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | cms-dev/tests/test_cms_cmp.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
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
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()
LEFTRIGHT

Powered by Google App Engine
This is Rietveld