from caffe2.proto import caffe2_pb2 from caffe2.python import core, workspace import os import tempfile from zipfile import ZipFile ''' Generates a document in markdown format summrizing the coverage of serialized testing. The document lives in `caffe2/python/serialized_test/SerializedTestCoverage.md` ''' OpSchema = workspace.C.OpSchema def gen_serialized_test_coverage(source_dir, output_dir): (covered, not_covered, schemaless) = gen_coverage_sets(source_dir) num_covered = len(covered) num_not_covered = len(not_covered) num_schemaless = len(schemaless) total_ops = num_covered + num_not_covered with open(os.path.join(output_dir, 'SerializedTestCoverage.md'), 'w+') as f: f.write('# Serialized Test Coverage Report\n') f.write("This is an automatically generated file. Please see " "`caffe2/python/serialized_test/README.md` for details. " "In the case of merge conflicts, please rebase and regenerate.\n") f.write('## Summary\n') f.write( 'Serialized tests have covered {}/{} ({}%) operators\n\n'.format( num_covered, total_ops, (int)(num_covered / total_ops * 1000) / 10)) f.write('## Not covered operators\n') f.write('
\n') f.write( 'There are {} not covered operators\n\n'.format( num_not_covered)) for n in sorted(not_covered): f.write('* ' + n + '\n') f.write('
\n\n') f.write('## Covered operators\n') f.write('
\n') f.write( 'There are {} covered operators\n\n'.format( num_covered)) for n in sorted(covered): f.write('* ' + n + '\n') f.write('
\n\n') f.write('## Excluded from coverage statistics\n') f.write('### Schemaless operators\n') f.write('
\n') f.write( 'There are {} schemaless operators\n\n'.format( num_schemaless)) for n in sorted(schemaless): f.write('* ' + n + '\n') f.write('
\n\n') def gen_coverage_sets(source_dir): covered_ops = gen_covered_ops(source_dir) not_covered_ops = set() schemaless_ops = [] for op_name in core._GetRegisteredOperators(): s = OpSchema.get(op_name) if s is not None and s.private: continue if s: if op_name not in covered_ops: not_covered_ops.add(op_name) else: if op_name.find("_ENGINE_") == -1: schemaless_ops.append(op_name) return (covered_ops, not_covered_ops, schemaless_ops) def gen_covered_ops(source_dir): def parse_proto(x): proto = caffe2_pb2.OperatorDef() proto.ParseFromString(x) return proto covered = set() for f in os.listdir(source_dir): zipfile = os.path.join(source_dir, f) if not os.path.isfile(zipfile): continue temp_dir = tempfile.mkdtemp() with ZipFile(zipfile) as z: z.extractall(temp_dir) op_path = os.path.join(temp_dir, 'op.pb') with open(op_path, 'rb') as f: loaded_op = f.read() op_proto = parse_proto(loaded_op) covered.add(op_proto.type) index = 0 grad_path = os.path.join(temp_dir, 'grad_{}.pb'.format(index)) while os.path.isfile(grad_path): with open(grad_path, 'rb') as f: loaded_grad = f.read() grad_proto = parse_proto(loaded_grad) covered.add(grad_proto.type) index += 1 grad_path = os.path.join(temp_dir, 'grad_{}.pb'.format(index)) return covered