Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(301)

Unified Diff: recipe_engine/test.py

Issue 2758923002: Handle unused recipe expectations in new 'test' command (Closed)
Patch Set: fixes Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | unittests/test_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: recipe_engine/test.py
diff --git a/recipe_engine/test.py b/recipe_engine/test.py
index c5e7112d99d83952240941a7e4d77d5161d10d16..42cc0a6cf2c057eeca62173995ff412a5016673b 100644
--- a/recipe_engine/test.py
+++ b/recipe_engine/test.py
@@ -17,6 +17,7 @@ import multiprocessing
import os
import pprint
import re
+import shutil
import signal
import sys
import tempfile
@@ -103,10 +104,12 @@ class CheckFailure(TestFailure):
class TestResult(object):
"""Result of running a test."""
- def __init__(self, test_description, failures, coverage_data):
+ def __init__(self, test_description, failures, coverage_data,
+ generates_expectation):
self.test_description = test_description
self.failures = failures
self.coverage_data = coverage_data
+ self.generates_expectation = generates_expectation
class TestDescription(object):
@@ -124,14 +127,16 @@ class TestDescription(object):
def full_name(self):
return '%s.%s' % (self.recipe_name, self.test_name)
+ @property
+ def expectation_path(self):
+ return os.path.join(self.expect_dir, self.test_name + '.json')
+
def run_test(test_description, train=False):
"""Runs a test. Returns TestResults object."""
expected = None
- expectation_path = os.path.join(
- test_description.expect_dir, test_description.test_name + '.json')
- if os.path.exists(expectation_path):
- with open(expectation_path) as f:
+ if os.path.exists(test_description.expectation_path):
+ with open(test_description.expectation_path) as f:
# TODO(phajdan.jr): why do we need to re-encode golden data files?
expected = re_encode(json.load(f))
@@ -148,10 +153,10 @@ def run_test(test_description, train=False):
failures.extend([CheckFailure(c) for c in failed_checks])
elif actual != expected:
if train:
- expectation_dir = os.path.dirname(expectation_path)
+ expectation_dir = os.path.dirname(test_description.expectation_path)
if not os.path.exists(expectation_dir):
os.makedirs(expectation_dir)
- with open(expectation_path, 'w') as f:
+ with open(test_description.expectation_path, 'wb') as f:
json.dump(
re_encode(actual), f, sort_keys=True, indent=2,
separators=(',', ': '))
@@ -169,7 +174,8 @@ def run_test(test_description, train=False):
sys.stdout.write('.')
sys.stdout.flush()
- return TestResult(test_description, failures, coverage_data)
+ return TestResult(test_description, failures, coverage_data,
+ actual is not None)
def run_recipe(recipe_name, test_name, covers):
@@ -384,6 +390,25 @@ def run_worker(test, train=False):
return run_test(test, train=train)
+def scan_for_expectations(root):
+ """Returns set of expectation paths recursively under |root|."""
+ collected_expectations = set()
+ for entry in os.listdir(root):
+ full_entry = os.path.join(root, entry)
+ if os.path.isdir(full_entry):
+ collected_expectations.update(scan_for_expectations(full_entry))
+ if not entry.endswith('.expected'):
+ continue
+ collected_expectations.add(full_entry)
+ if os.path.isdir(full_entry):
+ for subentry in os.listdir(full_entry):
+ if not subentry.endswith('.json'):
+ continue
+ full_subentry = os.path.join(full_entry, subentry)
+ collected_expectations.add(full_subentry)
+ return collected_expectations
+
+
def run_run(train, jobs):
"""Implementation of the 'run' command."""
start_time = datetime.datetime.now()
@@ -401,6 +426,8 @@ def run_run(train, jobs):
print()
+ used_expectations = set()
+
rc = 0
for success, details in results:
if success:
@@ -411,6 +438,10 @@ def run_run(train, jobs):
for failure in details.failures:
print(failure.format())
coverage_data.update(details.coverage_data)
+ if details.generates_expectation:
+ used_expectations.add(details.test_description.expectation_path)
+ used_expectations.add(
+ os.path.dirname(details.test_description.expectation_path))
else:
rc = 1
print('Internal failure:')
@@ -433,6 +464,29 @@ def run_run(train, jobs):
finally:
os.unlink(coverage_file.name)
+ actual_expectations = set()
+ if os.path.exists(_UNIVERSE_VIEW.recipe_dir):
+ actual_expectations.update(scan_for_expectations(_UNIVERSE_VIEW.recipe_dir))
+ if os.path.exists(_UNIVERSE_VIEW.module_dir):
+ for module_entry in os.listdir(_UNIVERSE_VIEW.module_dir):
+ if os.path.isdir(module_entry):
+ actual_expectations.update(scan_for_expectations(
+ os.path.join(_UNIVERSE_VIEW.module_dir, module_entry)))
+ unused_expectations = actual_expectations.difference(used_expectations)
+ if unused_expectations:
+ if train:
+ for entry in unused_expectations:
+ if not os.path.exists(entry):
+ continue
+ if os.path.isdir(entry):
+ shutil.rmtree(entry)
+ else:
+ os.unlink(entry)
+ else:
+ rc = 1
+ print('FATAL: unused expectations found:')
+ print('\n'.join(sorted(unused_expectations)))
+
finish_time = datetime.datetime.now()
print('-' * 70)
print('Ran %d tests in %0.3fs' % (
« no previous file with comments | « no previous file | unittests/test_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698