diff -r 0cc196c634f1 -r 0c686d10eb49 tests/subfiles.py --- a/tests/subfiles.py Wed May 29 16:36:23 2019 +0300 +++ b/tests/subfiles.py Thu May 30 12:03:53 2019 +0300 @@ -1,8 +1,10 @@ from testsuite import warning, error +import testsuite from geometry import * from os.path import dirname from pathlib import Path from configparser import ConfigParser +import math ini_path = Path(dirname(__file__)) / 'library-standards.ini' library_standards = ConfigParser() @@ -83,10 +85,72 @@ axes = interesting_axes, scaling = scaling) +def dependent_subfile_tests(model): + ''' + Tests subfile references for such qualities that are dependent on the + actual contents of the subfiles. Checks whether moved-to files are used. + Checks whether flat subfiles are scaled in the flat direction. + ''' + import filecache + cache = filecache.SubfileCache(model.ldraw_directories) + failed_subfiles = set() + for subfile_reference in model.subfile_references: + path = subfile_reference.subfile_path.lower() + if path in failed_subfiles: + # Already proven to be a bad apple, don't complain twice + pass + else: + try: + subfile = cache.prepare_file(path) + except filecache.CyclicalReferenceError as e: + failed_subfiles.add(path) + yield error(subfile_reference, 'cyclical-reference', + chain = str(e), + ) + if not subfile.valid: + yield error(subfile_reference, 'bad-subfile', + path = path, + problem_text = subfile.problem, + ) + failed_subfiles.add(path) + import re + match = re.search(r'^\~Moved(?: to (\w+))?$', subfile.description) + if match: + yield error(subfile_reference, 'moved-file-used', + moved_file = path, + new_file = match.group(1)) + scaling_vector = subfile_reference.matrix.scaling_vector() + scaled_dimensions = { + dimension + for dimension in subfile.flatness + if not math.isclose( + getattr(scaling_vector, dimension), + 1, + abs_tol = 1.0e-05 + ) + } + scaled_flat_dimensions = subfile.flatness & scaled_dimensions + if scaled_flat_dimensions: + yield testsuite.notice(subfile_reference, 'unnecessary-scaling', + scaled_flat_dimensions = scaled_flat_dimensions, + scaling_vector = scaling_vector, + ) + +def dimensions_description(dimensions): + sorted_dims = sorted(dimensions) + if len(sorted_dims) == 1: + return sorted_dims[0] + ' dimension' + else: + return str.format('{} and {} dimensions', + ', '.join(sorted_dims[:-1]), + sorted_dims[-1], + ) + manifest = { 'tests': { 'determinant': determinant_test, 'scaling-legality': scaling_legality_test, + 'dependent-subfiles': dependent_subfile_tests, }, 'messages': { 'zero-determinant': 'matrix determinant is zero ' @@ -96,5 +160,23 @@ primitive, scaling_description(scaling, axes), ), + 'cyclical-reference': lambda chain: + str.format('cyclical subfile dependency: {chain}', + **locals(), + ), + 'bad-subfile': lambda path, problem_text: + str.format('cannot process subfile "{path}": {problem_text}', + **locals(), + ), + 'moved-file-used': lambda moved_file, new_file: + str.format('subfile "{moved_file}" has been moved to "{new_file}"', + **locals(), + ), + 'unnecessary-scaling': lambda scaled_flat_dimensions, scaling_vector: + str.format( + 'subfile unnecessarily scaled in the {dims} ({scaling})', + dims = dimensions_description(scaled_flat_dimensions), + scaling = scaling_description(scaling_vector), + ) }, }