1import os 2import sys 3import difflib 4import builtins 5 6path = os.getenv('UNINSTALLED_INTROSPECTION_SRCDIR', None) 7assert path is not None 8sys.path.insert(0, path) 9 10# Not correct, but enough to get the tests going uninstalled 11builtins.__dict__['DATADIR'] = path 12builtins.__dict__['GIRDIR'] = '' 13 14from giscanner.annotationparser import GtkDocCommentBlockParser 15from giscanner.ast import Include, Namespace 16from giscanner.introspectablepass import IntrospectablePass 17from giscanner.maintransformer import MainTransformer 18from giscanner.message import MessageLogger 19from giscanner.sourcescanner import SourceScanner 20from giscanner.transformer import Transformer 21from giscanner.scannermain import process_packages 22 23 24currentdir = os.path.dirname(os.path.abspath(sys.argv[0])) 25current_name = os.path.basename(currentdir) 26path = os.path.abspath(os.path.join(currentdir, '..', '')) 27top_srcdir = os.environ['UNINSTALLED_INTROSPECTION_SRCDIR'] 28top_builddir = os.environ['TOP_BUILDDIR'] 29 30 31class ChunkedIO(object): 32 def __init__(self): 33 self.buffer = [] 34 35 def write(self, s): 36 self.buffer.append(s) 37 38 def getvalue(self): 39 return self.buffer 40 41 42class Options: 43 def __init__(self): 44 self.cpp_includes = [] 45 self.cpp_defines = [] 46 self.cpp_undefines = [] 47 self.library_paths = [] 48 49 50def _diff(a, b): 51 retval = '' 52 started = False 53 54 for group in difflib.SequenceMatcher(None, a, b).get_grouped_opcodes(3): 55 if not started: 56 started = True 57 retval += '--- expected\n' 58 retval += '+++ emitted\n' 59 60 for tag, i1, i2, j1, j2 in group: 61 if tag == 'equal': 62 for line in a[i1:i2]: 63 for l in line.split('\n'): 64 retval += ' ' + l + '\n' 65 continue 66 67 if tag in ('replace', 'delete'): 68 for line in a[i1:i2]: 69 for l in line.split('\n'): 70 retval += '-' + l + '\n' 71 72 if tag in ('replace', 'insert'): 73 for line in b[j1:j2]: 74 for l in line.split('\n'): 75 retval += '+' + l + '\n' 76 77 return retval 78 79 80def _extract_expected(filename): 81 fd = open(filename, 'r', encoding='utf-8') 82 data = fd.read() 83 fd.close() 84 85 retval = [] 86 for line in data.split('\n'): 87 if line.startswith('// EXPECT:'): 88 retval.append(line[10:] + '\n') 89 elif line.startswith('//+'): 90 retval[-1] += line[3:] + '\n' 91 92 return retval 93 94 95def check(args): 96 filename = args[0] 97 98 output = ChunkedIO() 99 namespace = Namespace('Test', '1.0') 100 logger = MessageLogger.get(namespace=namespace, output=output) 101 logger.enable_warnings(True) 102 103 transformer = Transformer(namespace) 104 transformer.set_include_paths([ 105 os.path.join(top_srcdir, 'gir'), 106 top_builddir, 107 os.path.join(top_builddir, 'gir'), 108 ]) 109 transformer.register_include(Include.from_string('GObject-2.0')) 110 111 ss = SourceScanner() 112 113 options = Options() 114 exit_code = process_packages(options, ['gobject-2.0']) 115 if exit_code: 116 sys.exit(exit_code) 117 ss.set_cpp_options(options.cpp_includes, options.cpp_defines, options.cpp_undefines) 118 ss.parse_files([filename]) 119 ss.parse_macros([filename]) 120 transformer.parse(ss.get_symbols()) 121 122 cbp = GtkDocCommentBlockParser() 123 blocks = cbp.parse_comment_blocks(ss.get_comments()) 124 125 main = MainTransformer(transformer, blocks) 126 main.transform() 127 128 final = IntrospectablePass(transformer, blocks) 129 final.validate() 130 131 emitted_warnings = [w[w.find(':') + 1:] for w in output.getvalue()] 132 133 expected_warnings = _extract_expected(filename) 134 135 sortkey = lambda x: int(x.split(':')[0]) 136 expected_warnings.sort(key=sortkey) 137 emitted_warnings.sort(key=sortkey) 138 139 if len(expected_warnings) != len(emitted_warnings): 140 raise SystemExit("ERROR in '%s': %d warnings were emitted, " 141 "expected %d:\n%s" % (os.path.basename(filename), 142 len(emitted_warnings), 143 len(expected_warnings), 144 _diff(expected_warnings, emitted_warnings))) 145 146 for emitted_warning, expected_warning in zip(emitted_warnings, expected_warnings): 147 if expected_warning != emitted_warning: 148 raise SystemExit("ERROR in '%s': expected warning does not match emitted " 149 "warning:\n%s" % (filename, 150 _diff([expected_warning], [emitted_warning]))) 151 152 153sys.exit(check(sys.argv[1:])) 154