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

Side by Side Diff: src/trusted/validator_arm/dgen_test_output.py

Issue 9960043: Finish separation of testing from sel_ldr validation. Also, automate (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client/
Patch Set: Created 8 years, 8 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 unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #!/usr/bin/python
2 #
3 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6 #
7
8 """
9 Responsible for generating the testing decoders based on
10 parsed table representations.
11 """
12
13 # This file generates testing code for our class decoder. The decoder
14 # tables are specifically written to minimize the number of decoder
15 # classes needed to parse valid ARM instructions. For testing, this is
16 # a problem. We can't (easily) tell if the intended instruction rules
17 # of ARM are being met, since there is not a one-to-one mapping from
18 # class decoders to rules.
19 #
20 # For example, consider the following two rows (from armv7.table):
21 #
22 # | 0011x - = Binary4RegisterShiftedOp
23 # Rsb_Rule_144_A1_P288
24 # cccc0000011snnnnddddssss0tt1mmmm
25 # RegsNotPc
26 # | 0100x - = Binary4RegisterShiftedOp
27 # Add_Rule_7_A1_P26
28 # cccc0000100snnnnddddssss0tt1mmmm
29 # RegsNotPc
30 #
31 # Both rows state to return a Binary4RegisterShiftedOp class decoder.
32 # The sequence of four symbols correspond to (in order presented):
33 #
34 # name - The name of the class decoder to use in sel_ldr
35 # rule - A unique name identifying the rule from the manual that
36 # defines what the selected class decoder is to decode.
37 # pattern - The sequence of bits defines by the rule (above)
38 # constraints - Any additional constraints assumed by the rule.
39 #
40 # All but the name is optional. The remaining fields provide
41 # additional documentation and information for testing (which is
42 # used by this file).
43 #
44 # If these two rows had a mergable bit pattern (which they do not),
45 # these rows would still not mergable since the actions are
46 # different. However, for sel_ldr, they both state to use a
47 # Binary4RegisterShiftedOp. The remaining identifiers are added data
48 # for testing only.
49 #
50 # We fix this by defining a notion of "action_filter" where one can
51 # choose to keep only those fields that are applicable. For sel_ldr,
52 # its only 'name'. For testing, it will include other fields, depending
53 # on the context.
54 #
55 # Note: The current ARM instruction table has both new and old
56 # actions. Old actions only define the 'InstClass' entry. If the
57 # remaining fields are omitted, the corresponding testing for those
58 # entries are omitted.
59 #
60 # Note: See dgen_decoder_output.py for more details on how we build a
61 # decoder for sel_ldr.
62 #
63 # For testing, we would like to know the specific instruction rule
64 # that was being tested. Further, we would like to know what
65 # instruction rule was chosen for each decoder class selection made by
66 # the parse tables. To do this, we do two levels of wrapping.
67 #
68 # This file generates a set of wrapper classes, each a subclass of
69 # NamedClassDecoder. One is generated for each InstClass needed by
70 # sel_ldr (i.e. only the 'name' field). These named classes correspond
71 # to what sel_ldr will select.
72 #
73 # The named version of each named InstClass is:
74 #
75 # class NamedInstClass : public NamedClassDecoder {
76 # public:
77 # inline NamedInstClass()
78 # : NamedClassDecoder(decoder_, "InstClass")
79 # {}
80 # virtual ~NamedInstClass() {}
81 # protected:
82 # explicit inline NamedInstClass(const char* name)
83 # : NamedClassDecoder(decoder_, name) {}
84 # private:
85 # Binary3RegisterShiftedTest decoder_;
86 #};
87 #
88 # This makes sure that each decoder class can be identified using a
89 # separate class decoder. The public constructor is for table rows
90 # that don't have rule names. The protected constructor is for table
91 # rows that have a rule name, and will be a subclass of this class.
92 # The class defined for rows with a Rule name is:
93 #
94 # class NamedRuleInstClass : public NamedInstClass {
95 # public:
96 # inline NamedRuleInstClass()
97 # : NamedInstClass("RuleInstClass")
98 # {}
99 # virtual ~NamedAdd_Rule_7_A1_P26Binary4RegisterShiftedOp() {}
100 #};
101 #
102 # The base class for NamedClassDecoder is specified in
103 # "named_class_decoder.h". This file defines a class that takes a
104 # ClassDecoder (reference) C and a print name NAME, and builds a
105 # corresponding ClassDecoder that acts like C, but will print out
106 # NAME. The behaviour of C is maintained by dispatching each virtual
107 # on the NamedClassDecoder to the corresponding virtual on C.
108 #
109 # We then define the class decoder Decoder, by defining a derived
110 # instance of DecoderState as follows:
111 #
112 # class NamedDecoder : DecoderState {
113 # public:
114 # explicit NamedDecoder();
115 # virtual ~NamedDecoder();
116 # const NamedClassDecoder& decode_named(const Instruction) const;
117 # virtual const ClassDecoder& decode(const Instruction) const;
118 # ...
119 # };
120 #
121 # The method decode is the expected API for the NamedDecoder, which is
122 # an instance of DecoderState (defined in decode.h). The method
123 # decode_named is the same, but returns NamedClassDecoder's so that
124 # good error messages can be generated by the test harnesses for
125 # ClassDecoder's (see decoder_tester.h for more details on
126 # ClassDecoder test harnesses).
127 #
128 # To the NamedDecoder, we add a constant field NamedClassDecoder for
129 # each possible class decoder method decode_named could return, or
130 # that we could use in automatically generated tests. These fields
131 # allow us to only create the corresponding decoder classes once
132 # (during constructor initialization).
133 #
134 # Finally, we add a method corresponding to each defined decoder
135 # table. The forms of these decoders is:
136 #
137 # inline const NamedClassDecoder& decode_TABLE(
138 # const nacl_arm_dec::Instruction insn) const;
139 #
140 # Each of these methods are defined as inline methods so that they can
141 # be optimized away in the corresponding top level methods (i.e.
142 # decode_named and decode).
143 #
144 # For testing, there are three files generated:
145 #
146 # decoder_named_classes.h
147 # decoder_named_decoder.h
148 # decoder_named.cc
149 # decoder_tests.cc
150 #
151 # File decoder_named_classes.h defines the class declarations for the
152 # generated Rule classes, and named class decoder classes. File
153 # decoder_named_decoder.h defines the decoder class NamedDecoder
154 # (discussed above). decoder_named.cc contains the corresponding
155 # implementations of the constructors and methods of these classes.
156 #
157 # decoder_tests.cc generates an automatic test harness executable,
158 # that will test each instruction Rule. Each test generates all
159 # possible matches the the corresponding Pattern of the table rule,
160 # and calls the corresponding tester associated with the class decoder
161 # of that row. By default, the tester is presumed to be named.
162 #
163 # InstClassTester
164 #
165 # If the row defines a Constraints identifier, then the tester
166 #
167 # InstClassTesterConstraints
168 #
169 # is used instead.
170
171 import dgen_opt
172 import dgen_output
173
174 # Defines the header for decoder_named_classes.h
175 NAMED_CLASSES_H_HEADER="""
176 %(FILE_HEADER)s
177 %(NOT_TCB_MESSAGE)s
178
179 #ifndef %(IFDEF_NAME)s
180 #define %(IFDEF_NAME)s
181
182 #include "native_client/src/trusted/validator_arm/named_class_decoder.h"
183
184 """
185
186 RULE_CLASSES_HEADER="""
187 /*
188 * Define rule decoder classes.
189 */
190 namespace nacl_arm_dec {
191 """
192
193 RULE_CLASS="""
194 class %(rule)s%(decoder)s
195 : public %(decoder)s {
196 public:
197 virtual ~%(rule)s%(decoder)s() {}
198 };
199
200 """
201
202 RULE_CLASSES_FOOTER="""
203 } // nacl_arm_dec
204 """
205
206 NAMED_H_NAMESPACE="""
207 namespace nacl_arm_test {
208 """
209
210 NAMED_DECODERS_HEADER="""
211 /*
212 * Define named class decoders for each class decoder.
213 * The main purpose of these classes is to introduce
214 * instances that are named specifically to the class decoder
215 * and/or rule that was used to parse them. This makes testing
216 * much easier in that error messages use these named classes
217 * to clarify what row in the corresponding table was used
218 * to select this decoder. Without these names, debugging the
219 * output of the test code would be nearly impossible
220 */
221
222 """
223
224 NAMED_DECODER_DECLARE="""
225 class Named%(decoder)s : public NamedClassDecoder {
226 public:
227 inline Named%(decoder)s()
228 : NamedClassDecoder(decoder_, "%(decoder)s")
229 {}
230 virtual ~Named%(decoder)s() {}
231 protected:
232 explicit inline Named%(decoder)s(const char* name)
233 : NamedClassDecoder(decoder_, name) {}
234 private:
235 nacl_arm_dec::%(decoder)s decoder_;
236 };
237
238 """
239
240 NAMED_RULE_DECLARE="""
241 class Named%(rule)s%(decoder)s
242 : public Named%(decoder)s {
243 public:
244 inline Named%(rule)s%(decoder)s()
245 : Named%(decoder)s("%(rule)s%(decoder)s")
246 {}
247 virtual ~Named%(rule)s%(decoder)s() {}
248 };
249
250 """
251
252 NAMED_CLASSES_H_FOOTER="""
253 } // namespace nacl_arm_test
254 #endif // %(IFDEF_NAME)s
255 """
256
257 def generate_named_classes_h(decoder, decoder_name, filename, out):
258 """Defines named classes needed for decoder testing.
259
260 Args:
261 tables: list of Table objects to process.
262 decoder_name: The name of the decoder state to build.
263 filename: The (localized) name for the .h file.
264 out: a COutput object to write to.
265 """
266 if not decoder.primary: raise Exception('No tables provided.')
267
268 values = {
269 'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
270 'NOT_TCB_MESSAGE' : dgen_output.NOT_TCB_BOILERPLATE,
271 'IFDEF_NAME' : dgen_output.ifdef_name(filename),
272 'decoder_name': decoder_name,
273 }
274 out.write(NAMED_CLASSES_H_HEADER % values)
275 _generate_rule_classes(decoder, values, out)
276 out.write(NAMED_H_NAMESPACE)
277 _generate_named_decoder_classes(decoder, values, out)
278 out.write(NAMED_CLASSES_H_FOOTER % values)
279
280 def _generate_named_decoder_classes(decoder, values, out):
281 out.write(NAMED_DECODERS_HEADER)
282 # Generate one for each type of decoder in the decoder.
283 for d in decoder.action_filter(['name']).decoders():
284 values['decoder'] = d.name
285 values['rule'] = ''
286 out.write(NAMED_DECODER_DECLARE % values)
287 # Now generate one for each decoder that has a rule associated with it.
288 for d in decoder.action_filter(['name', 'rule']).rules():
289 values['decoder'] = d.name
290 values['rule'] = d.rule
291 out.write(NAMED_RULE_DECLARE % values)
292
293 def _generate_rule_classes(decoder, values, out):
294 # Note: we generate these classes in nacl_arm_dec, so that
295 # all decoder classes generated by the pareser are in the
296 # same namesapce.
297 out.write(RULE_CLASSES_HEADER)
298 for action in decoder.action_filter(['name', 'rule']).rules():
299 values['decoder'] = action.name
300 values['rule'] = action.rule
301 out.write(RULE_CLASS % values)
302 out.write(RULE_CLASSES_FOOTER)
303
304 NAMED_DECODER_H_HEADER="""
305 %(FILE_HEADER)s
306 %(NOT_TCB_MESSAGE)s
307
308 #ifndef %(IFDEF_NAME)s
309 #define %(IFDEF_NAME)s
310
311 #include "native_client/src/trusted/validator_arm/decode.h"
312 #include "%(FILENAME_BASE)s_classes.h"
313 #include "native_client/src/trusted/validator_arm/named_class_decoder.h"
314
315 namespace nacl_arm_test {
316 """
317
318 DECODER_STATE_HEADER="""
319 // Defines a stateless (named)decoder class selector for instructions
320 class Named%(decoder_name)s : nacl_arm_dec::DecoderState {
321 public:
322 explicit Named%(decoder_name)s();
323 virtual ~Named%(decoder_name)s();
324
325 // Parses the given instruction, returning the named class
326 // decoder to use.
327 const NamedClassDecoder& decode_named(
328 const nacl_arm_dec::Instruction) const;
329
330 // Parses the given instruction, returning the class decoder
331 // to use.
332 virtual const nacl_arm_dec::ClassDecoder& decode(
333 const nacl_arm_dec::Instruction) const;
334
335 // Fields containing the named class decoders that can
336 // be returned.
337 """
338
339 DECODER_STATE_FIELD_COMMENTS="""
340 // The following fields define the set of class decoders
341 // that can be returned by the API function "decode". They
342 // are created once as instance fields, and then returned
343 // by the table methods above. This speeds up the code since
344 // the class decoders need to only be bulit once (and reused
345 // for each call to "decode").
346 """
347
348 DECODER_STATE_FIELD="""
349 const Named%(rule)s%(decoder)s %(rule)s%(decoder)s_instance_;
350 """
351
352 DECODER_STATE_PRIVATE="""
353 private:
354 """
355
356 DECODER_STATE_DECODER_COMMENTS="""
357 // The following list of methods correspond to each decoder table,
358 // and implements the pattern matching of the corresponding bit
359 // patterns. After matching the corresponding bit patterns, they
360 // either call other methods in this list (corresponding to another
361 // decoder table), or they return the instance field that implements
362 // the class decoder that should be used to decode the particular
363 // instruction.
364 """
365
366 DECODER_STATE_DECODER="""
367 inline const NamedClassDecoder& decode_%(table)s(
368 const nacl_arm_dec::Instruction insn) const;
369 """
370
371 DECODER_STATE_FOOTER="""
372 };
373 """
374
375 NAMED_DECODER_H_FOOTER="""
376 } // namespace nacl_arm_test
377 #endif // %(IFDEF_NAME)s
378 """
379
380 def generate_named_decoder_h(decoder, decoder_name, filename, out):
381 """Generates the named decoder for testing.
382
383 Args:
384 tables: list of Table objects to process.
385 decoder_name: The name of the decoder state to build.
386 filename: The (localized) name for the .h file.
387 out: a COutput object to write to.
388 """
389 if not decoder.primary: raise Exception('No tables provided.')
390 assert filename.endswith('_decoder.h')
391
392 values = {
393 'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
394 'NOT_TCB_MESSAGE' : dgen_output.NOT_TCB_BOILERPLATE,
395 'IFDEF_NAME' : dgen_output.ifdef_name(filename),
396 'FILENAME_BASE': filename[:-len('_decoder.h')],
397 'decoder_name': decoder_name,
398 }
399 out.write(NAMED_DECODER_H_HEADER % values)
400 _generate_decoder_state_class(decoder, values, out)
401 out.write(NAMED_DECODER_H_FOOTER % values)
402
403 def _generate_decoder_state_class(decoder, values, out):
404 # Generate a field for each type of decoder in the decoder.
405 out.write(DECODER_STATE_HEADER % values)
406 out.write(DECODER_STATE_FIELD_COMMENTS);
407 for d in decoder.action_filter(['name']).decoders():
408 values['decoder'] = d.name
409 values['rule'] = ''
410 out.write(DECODER_STATE_FIELD % values)
411 # Now generate one for each decoder that has a rule associated with it.
412 for d in decoder.action_filter(['name', 'rule']).rules():
413 values['decoder'] = d.name
414 values['rule'] = d.rule
415 out.write(DECODER_STATE_FIELD % values)
416 out.write(DECODER_STATE_PRIVATE);
417 out.write(DECODER_STATE_DECODER_COMMENTS)
418 for table in decoder.tables():
419 values['table'] = table.name
420 out.write(DECODER_STATE_DECODER % values)
421 out.write(DECODER_STATE_FOOTER % values)
422
423 # Defines the source for DECODER_named.cc
424 NAMED_CC_HEADER="""
425 %(FILE_HEADER)s
426 %(NOT_TCB_MESSAGE)s
427 #include "%(FILENAME_BASE)s_decoder.h"
428
429 #include <stdio.h>
430
431 using nacl_arm_dec::ClassDecoder;
432 using nacl_arm_dec::Instruction;
433
434 namespace nacl_arm_test {
435 """
436
437 PARSE_CONSTRUCT_HEADER="""
438 Named%(decoder_name)s::Named%(decoder_name)s()
439 : nacl_arm_dec::DecoderState()
440 """
441
442 PARSE_CONSTRUCT_FIELDS="""
443 , %(rule)s%(decoder)s_instance_()
444 """
445
446 PARSE_CONSTRUCT_FOOTER="""
447 {}
448
449 Named%(decoder_name)s::~Named%(decoder_name)s() {}
450 """
451
452 PARSE_TABLE_METHOD_HEADER="""
453 /*
454 * Implementation of table %(table_name)s.
455 * Specified by: %(citation)s
456 */
457 const NamedClassDecoder& Named%(decoder_name)s::decode_%(table_name)s(
458 const nacl_arm_dec::Instruction insn) const {
459 """
460
461 PARSE_TABLE_METHOD_ROW="""
462 if (%(tests)s) {
463 return %(action)s;
464 }
465 """
466
467 PARSE_TABLE_METHOD_FOOTER="""
468 // Catch any attempt to fall through...
469 fprintf(stderr, "TABLE IS INCOMPLETE: %(table_name)s could not parse %%08X",
470 insn.bits(31,0));
471 return Forbidden_instance_;
472 }
473
474 """
475
476 NAMED_CC_FOOTER="""
477 const NamedClassDecoder& Named%(decoder_name)s::
478 decode_named(const nacl_arm_dec::Instruction insn) const {
479 return decode_%(entry_table_name)s(insn);
480 }
481
482 const nacl_arm_dec::ClassDecoder& Named%(decoder_name)s::
483 decode(const nacl_arm_dec::Instruction insn) const {
484 return decode_named(insn).named_decoder();
485 }
486
487 } // namespace nacl_arm_test
488 """
489
490 def generate_named_cc(decoder, decoder_name, filename, out):
491 """Implementation of the test decoder in .cc file
492
493 Args:
494 tables: list of Table objects to process.
495 decoder_name: The name of the decoder state to build.
496 filename: The (localized) name for the .h file.
497 out: a COutput object to write to.
498 """
499 if not decoder.primary: raise Exception('No tables provided.')
500 assert filename.endswith('.cc')
501
502 values = {
503 'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
504 'NOT_TCB_MESSAGE' : dgen_output.NOT_TCB_BOILERPLATE,
505 'FILENAME_BASE' : filename[:-len('.cc')],
506 'decoder_name': decoder_name,
507 'entry_table_name': decoder.primary.name,
508 }
509 out.write(NAMED_CC_HEADER % values)
510 _generate_decoder_constructors(decoder, values, out)
511 _generate_decoder_method_bodies(decoder, values, out)
512 out.write(NAMED_CC_FOOTER % values)
513
514 def _generate_decoder_constructors(decoder, values, out):
515 out.write(PARSE_CONSTRUCT_HEADER % values)
516 # Initialize each type of decoder in the decoder.
517 for d in decoder.action_filter(['name']).decoders():
518 values['decoder'] = d.name
519 values['rule'] = ''
520 out.write(PARSE_CONSTRUCT_FIELDS % values)
521 # Now initialize fields for each decoder with a rule.
522 for d in decoder.action_filter(['name', 'rule']).rules():
523 values['decoder'] = d.name
524 values['rule'] = d.rule
525 out.write(PARSE_CONSTRUCT_FIELDS % values)
526 out.write(PARSE_CONSTRUCT_FOOTER % values)
527
528 def _generate_decoder_method_bodies(decoder, values, out):
529 for table in decoder.tables():
530 opt_rows = dgen_opt.optimize_rows(
531 table.action_filter(['name', 'rule']).rows)
532 print ("Table %s: %d rows minimized to %d"
533 % (table.name, len(table.rows), len(opt_rows)))
534
535 values['table_name'] = table.name
536 values['citation'] = table.citation,
537 out.write(PARSE_TABLE_METHOD_HEADER % values)
538
539 # Add message to stop compilation warnings if this table
540 # doesn't require subtables to select a class decoder.
541 if not [r for r in opt_rows
542 if r.action.__class__.__name__ == 'DecoderMethod']:
543 out.write(" UNREFERENCED_PARAMETER(insn);")
544
545 for row in opt_rows:
546 if row.action.__class__.__name__ == 'DecoderAction':
547 values['decoder'] = row.action.name
548 values['rule'] = row.action.rule if row.action.rule else ''
549 action = '%(rule)s%(decoder)s_instance_' % values
550 elif row.action.__class__.__name__ == 'DecoderMethod':
551 action = 'decode_%s(insn)' % row.action.name
552 else:
553 raise Exception('Bad table action: %s' % row.action)
554 # Each row consists of a set of bit patterns defining if the row
555 # is applicable. Convert this into a sequence of anded C test
556 # expressions. For example, convert the following pair of bit
557 # patterns:
558 #
559 # xxxx1010xxxxxxxxxxxxxxxxxxxxxxxx
560 # xxxxxxxxxxxxxxxxxxxxxxxxxxxx0101
561 #
562 # Each instruction is masked to get the the bits, and then
563 # tested against the corresponding expected bits. Hence, the
564 # above example is converted to:
565 #
566 # ((insn & 0x0F000000) != 0x0C000000) &&
567 # ((insn & 0x0000000F) != 0x00000005)
568 values['tests'] = ' && '.join(['(%s)' % p.to_c_expr('insn')
569 for p in row.patterns])
570 values['action'] = action
571 out.write(PARSE_TABLE_METHOD_ROW % values)
572 out.write(PARSE_TABLE_METHOD_FOOTER % values)
573
574 # Define the source for DECODER_tests.cc
575 TEST_CC_HEADER="""
576 %(FILE_HEADER)s
577 %(NOT_TCB_MESSAGE)s
578
579 #include "gtest/gtest.h"
580 #include "native_client/src/trusted/validator_arm/inst_classes_testers.h"
581
582 namespace nacl_arm_test {
583
584 """
585
586 TESTER_CLASS="""
587 class %(rule)s%(decoder)sTester%(constraints)s
588 : public %(decoder)sTester%(constraints)s {
589 public:
590 %(rule)s%(decoder)sTester%(constraints)s()
591 : %(decoder)sTester%(constraints)s(
592 state_.%(rule)s%(decoder)s_instance_)
593 {}
594 };
595 """
596
597 TEST_HARNESS="""
598 // Defines a gtest testing harness for tests.
599 class %(decoder_name)sTests : public ::testing::Test {
600 protected:
601 %(decoder_name)sTests() {}
602 };
603 """
604
605 TEST_FUNCTION="""
606 TEST_F(%(decoder_name)sTests,
607 %(rule)s%(decoder)s%(constraints)s_%(pattern)s_Test) {
608 %(rule)s%(decoder)sTester%(constraints)s tester;
609 tester.Test("%(pattern)s");
610 }
611 """
612
613
614 TEST_CC_FOOTER="""
615 } // namespace nacl_arm_test
616
617 int main(int argc, char* argv[]) {
618 testing::InitGoogleTest(&argc, argv);
619 return RUN_ALL_TESTS();
620 }
621 """
622
623 def generate_tests_cc(decoder, decoder_name, out):
624 if not decoder.primary: raise Exception('No tables provided.')
625 values = {
626 'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
627 'NOT_TCB_MESSAGE' : dgen_output.NOT_TCB_BOILERPLATE,
628 'decoder_name': decoder_name,
629 }
630 out.write(TEST_CC_HEADER % values)
631 _generate_rule_testers(decoder, values, out)
632 out.write(TEST_HARNESS % values)
633 _generate_test_patterns(decoder, values, out)
634 out.write(TEST_CC_FOOTER % values)
635
636 def _generate_rule_testers(decoder, values, out):
637 for d in decoder.action_filter(['name', 'rule', 'constraints']).rules():
638 values['decoder'] = d.name
639 values['rule'] = d.rule
640 values['constraints'] = d.constraints if d.constraints else ''
641 out.write(TESTER_CLASS % values)
642
643 def _generate_test_patterns(decoder, values, out):
644 for d in decoder.decoders():
645 if d.pattern:
646 values['decoder'] = d.name
647 values['rule'] = d.rule if d.rule else ''
648 values['constraints'] = d.constraints if d.constraints else ''
649 values['pattern'] = d.pattern
650 out.write(TEST_FUNCTION % values)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698