tests/subfiles.py

changeset 62
f0a6bf48b05e
parent 55
388df1fa18a2
child 64
1c0884f5506e
equal deleted inserted replaced
61:15c95d3fcfd8 62:f0a6bf48b05e
1 from testsuite import warning, error 1 from testsuite import problem_type, report_problem
2 import testsuite 2 import testsuite
3 from geometry import * 3 from geometry import *
4 from os.path import dirname 4 from os.path import dirname
5 from pathlib import Path 5 from pathlib import Path
6 from configparser import ConfigParser 6 from configparser import ConfigParser
9 library_standards = ConfigParser() 9 library_standards = ConfigParser()
10 10
11 with ini_path.open() as file: 11 with ini_path.open() as file:
12 library_standards.read_file(file) 12 library_standards.read_file(file)
13 13
14 @problem_type('zero-determinant',
15 severity = 'error',
16 message = 'matrix row or column all zero'
17 )
14 def determinant_test(model): 18 def determinant_test(model):
15 ''' 19 '''
16 Checks all subfile references for matrices with rows or columns all 20 Checks all subfile references for matrices with rows or columns all
17 zero. 21 zero.
18 ''' 22 '''
19 yield from ( 23 yield from (
20 error(subfile_reference, 'zero-determinant') 24 report_problem('zero-determinant', bad_object = subfile_reference)
21 for subfile_reference in model.subfile_references 25 for subfile_reference in model.subfile_references
22 if abs(subfile_reference.matrix.determinant() - 0) < 1e-15 26 if abs(subfile_reference.matrix.determinant() - 0) < 1e-15
23 ) 27 )
24 28
25 def scaling_description(scaling, axes = 'xyz'): 29 def scaling_description(scaling, axes = 'xyz'):
26 ''' 30 '''
27 Returns a pretty description of a scaling vector. The axes parameter 31 Returns a pretty description of a scaling vector. The axes parameter
28 controls what axes are printed and can be used to filter away 32 controls what axes are printed and can be used to filter away
29 uninteresting values. 33 uninteresting values.
30 ''' 34 '''
31 return ', '.join( 35 if isinstance(scaling, str):
32 str.format('{} = {}', letter, getattr(scaling, letter)) 36 return scaling
33 for letter in axes 37 else:
34 ) 38 return ', '.join(
39 str.format('{} = {}', letter, getattr(scaling, letter))
40 for letter in axes
41 )
35 42
36 def check_scaling(scaling, axes): 43 def check_scaling(scaling, axes):
37 ''' Returns whether all given axes on the given scaling vector are 1. ''' 44 ''' Returns whether all given axes on the given scaling vector are 1. '''
38 return all( 45 return all(
39 abs(getattr(scaling, axis) - 1) < 1e-5 46 abs(getattr(scaling, axis) - 1) < 1e-5
49 # check if y-scaling is 1 or -1 56 # check if y-scaling is 1 or -1
50 abs(abs(scaling.y) - 1) < 1e-5, 57 abs(abs(scaling.y) - 1) < 1e-5,
51 ]), 58 ]),
52 } 59 }
53 60
61 @problem_type('illegal-scaling',
62 severity = 'error',
63 message = lambda primitive, scaling, axes:
64 str.format('scaling of unscalable primitive {} ({})',
65 primitive,
66 scaling_description(scaling, axes),
67 ),
68 )
54 def scaling_legality_test(model): 69 def scaling_legality_test(model):
55 ''' 70 '''
56 Checks the part against primitive references with bad scaling. Some 71 Checks the part against primitive references with bad scaling. Some
57 primitives (e.g. pegs) are not allowed to be scaled in the 72 primitives (e.g. pegs) are not allowed to be scaled in the
58 X or Z directions. Some (e.g. most studs) are not allowed to be scaled 73 X or Z directions. Some (e.g. most studs) are not allowed to be scaled
78 interesting_axes = ''.join( 93 interesting_axes = ''.join(
79 axis 94 axis
80 for axis in 'xyz' 95 for axis in 'xyz'
81 if abs(getattr(scaling, axis) - 1) > 1e-5 96 if abs(getattr(scaling, axis) - 1) > 1e-5
82 ) 97 )
83 yield warning(subfile_reference, 'illegal-scaling', 98 yield report_problem('illegal-scaling',
99 bad_object = subfile_reference,
84 primitive = primitive, 100 primitive = primitive,
85 axes = interesting_axes, 101 axes = interesting_axes,
86 scaling = scaling) 102 scaling = scaling,
87 103 )
104
105 @problem_type('cyclical-reference',
106 severity = 'error',
107 message = lambda chain:
108 str.format('cyclical subfile dependency: {chain}',
109 **locals(),
110 ),
111 )
112 @problem_type('bad-subfile',
113 severity = 'error',
114 message = lambda path, problem_text:
115 str.format('cannot process subfile "{path}": {problem_text}',
116 **locals(),
117 ),
118 )
119 @problem_type('moved-file-used',
120 severity = 'error',
121 message = lambda moved_file, new_file:
122 str.format('subfile "{moved_file}" has been moved to "{new_file}"',
123 **locals(),
124 ),
125 )
126 @problem_type('unnecessary-scaling',
127 severity = 'notice',
128 message = lambda scaled_flat_dimensions, scaling_vector:
129 str.format(
130 'subfile unnecessarily scaled in the {dims} ({scaling})',
131 dims = dimensions_description(scaled_flat_dimensions),
132 scaling = scaling_description(scaling_vector),
133 ),
134 )
88 def dependent_subfile_tests(model): 135 def dependent_subfile_tests(model):
89 ''' 136 '''
90 Tests subfile references for such qualities that are dependent on the 137 Tests subfile references for such qualities that are dependent on the
91 actual contents of the subfiles. Checks whether moved-to files are used. 138 actual contents of the subfiles. Checks whether moved-to files are used.
92 Checks whether flat subfiles are scaled in the flat direction. 139 Checks whether flat subfiles are scaled in the flat direction.
102 else: 149 else:
103 try: 150 try:
104 subfile = cache.prepare_file(path) 151 subfile = cache.prepare_file(path)
105 except filecache.CyclicalReferenceError as e: 152 except filecache.CyclicalReferenceError as e:
106 failed_subfiles.add(path) 153 failed_subfiles.add(path)
107 yield error(subfile_reference, 'cyclical-reference', 154 yield report_problem(
155 'cyclical-reference',
156 bad_object = subfile_reference,
108 chain = str(e), 157 chain = str(e),
109 ) 158 )
110 if not subfile.valid: 159 if not subfile.valid:
111 yield error(subfile_reference, 'bad-subfile', 160 yield report_problem(
161 'bad-subfile',
162 bad_object = subfile_reference,
112 path = path, 163 path = path,
113 problem_text = subfile.problem, 164 problem_text = subfile.problem,
114 ) 165 )
115 failed_subfiles.add(path) 166 failed_subfiles.add(path)
116 else: 167 else:
117 import re 168 import re
118 match = re.search(r'^\~Moved(?: to (\w+))?$', subfile.description) 169 match = re.search(r'^\~Moved(?: to (\w+))?$', subfile.description)
119 if match: 170 if match:
120 yield error(subfile_reference, 'moved-file-used', 171 yield report_problem(
172 'moved-file-used',
173 bad_object = subfile_reference,
121 moved_file = path, 174 moved_file = path,
122 new_file = match.group(1)) 175 new_file = match.group(1),
176 )
123 scaling_vector = subfile_reference.matrix.scaling_vector() 177 scaling_vector = subfile_reference.matrix.scaling_vector()
124 scaled_dimensions = { 178 scaled_dimensions = {
125 dimension 179 dimension
126 for dimension in subfile.flatness 180 for dimension in subfile.flatness
127 if not math.isclose( 181 if not math.isclose(
130 abs_tol = 1.0e-05 184 abs_tol = 1.0e-05
131 ) 185 )
132 } 186 }
133 scaled_flat_dimensions = subfile.flatness & scaled_dimensions 187 scaled_flat_dimensions = subfile.flatness & scaled_dimensions
134 if scaled_flat_dimensions: 188 if scaled_flat_dimensions:
135 yield testsuite.notice(subfile_reference, 'unnecessary-scaling', 189 yield report_problem(
190 'unnecessary-scaling',
191 bad_object = subfile_reference,
136 scaled_flat_dimensions = scaled_flat_dimensions, 192 scaled_flat_dimensions = scaled_flat_dimensions,
137 scaling_vector = scaling_vector, 193 scaling_vector = scaling_vector,
138 ) 194 )
139 195
140 def dimensions_description(dimensions): 196 def dimensions_description(dimensions):
141 sorted_dims = sorted(dimensions) 197 if isinstance(dimensions, str):
142 if len(sorted_dims) == 1: 198 return dimensions
143 return sorted_dims[0] + ' dimension'
144 else: 199 else:
145 return str.format('{} and {} dimensions', 200 sorted_dims = sorted(dimensions)
146 ', '.join(sorted_dims[:-1]), 201 if len(sorted_dims) == 1:
147 sorted_dims[-1], 202 return sorted_dims[0] + ' dimension'
148 ) 203 else:
204 return str.format('{} and {} dimensions',
205 ', '.join(sorted_dims[:-1]),
206 sorted_dims[-1],
207 )
149 208
150 manifest = { 209 manifest = {
151 'tests': { 210 'tests': [
152 'determinant': determinant_test, 211 determinant_test,
153 'scaling-legality': scaling_legality_test, 212 scaling_legality_test,
154 'dependent-subfiles': dependent_subfile_tests, 213 dependent_subfile_tests,
155 }, 214 ],
156 'messages': {
157 'zero-determinant': 'matrix determinant is zero '
158 '(row or column all zero)',
159 'illegal-scaling': lambda primitive, scaling, axes:
160 str.format('scaling of unscalable primitive {} ({})',
161 primitive,
162 scaling_description(scaling, axes),
163 ),
164 'cyclical-reference': lambda chain:
165 str.format('cyclical subfile dependency: {chain}',
166 **locals(),
167 ),
168 'bad-subfile': lambda path, problem_text:
169 str.format('cannot process subfile "{path}": {problem_text}',
170 **locals(),
171 ),
172 'moved-file-used': lambda moved_file, new_file:
173 str.format('subfile "{moved_file}" has been moved to "{new_file}"',
174 **locals(),
175 ),
176 'unnecessary-scaling': lambda scaled_flat_dimensions, scaling_vector:
177 str.format(
178 'subfile unnecessarily scaled in the {dims} ({scaling})',
179 dims = dimensions_description(scaled_flat_dimensions),
180 scaling = scaling_description(scaling_vector),
181 )
182 },
183 } 215 }

mercurial