mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
It was originally designed for faster trasmission of syntax trees from C++ to SwiftSyntax, but superceded by the CLibParseActions. There's no deserializer for it anymore, so let's just remove it.
153 lines
6.5 KiB
Python
Executable File
153 lines
6.5 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import difflib
|
|
import io
|
|
import os
|
|
import sys
|
|
|
|
from test_util import TestFailedError, serializeIncrParseMarkupFile
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
description='Utility for testing incremental syntax parsing',
|
|
epilog='''
|
|
Based of a single template the utility generates a pre-edit and a post-edit
|
|
file. It then verifies that incrementally parsing the post-edit file base
|
|
on the pre-edit file results in the same syntax tree as reparsing the
|
|
post-edit file from scratch.
|
|
|
|
To generate the pre-edit and the post-edit file from the template, it
|
|
operates on markers of the form:
|
|
|
|
<<test_case<pre|||post>>>
|
|
|
|
These placeholders are replaced by:
|
|
- 'pre' if a different test case than 'test_case' is run
|
|
- 'pre' for the pre-edit version of 'test_case'
|
|
- 'post' for the post-edit version of 'test_case'
|
|
''')
|
|
parser.add_argument(
|
|
'file', type=argparse.FileType(),
|
|
help='The template file to test')
|
|
parser.add_argument(
|
|
'--test-case', default='',
|
|
help='The test case to execute. If no test case is specified all \
|
|
unnamed substitutions are applied')
|
|
parser.add_argument(
|
|
'--temp-dir', required=True,
|
|
help='A temporary directory where pre-edit and post-edit files can be \
|
|
saved')
|
|
parser.add_argument(
|
|
'--swift-syntax-test', required=True,
|
|
help='The path to swift-syntax-test')
|
|
parser.add_argument(
|
|
'--print-visual-reuse-info', default=False, action='store_true',
|
|
help='Print visual reuse information about the incremental parse \
|
|
instead of diffing the syntax trees. This option is intended \
|
|
for debug purposes only.')
|
|
|
|
args = parser.parse_args(sys.argv[1:])
|
|
|
|
test_file = args.file.name
|
|
test_file_name = os.path.basename(test_file)
|
|
test_case = args.test_case
|
|
temp_dir = args.temp_dir
|
|
swift_syntax_test = args.swift_syntax_test
|
|
visual_reuse_info = args.print_visual_reuse_info
|
|
|
|
if not os.path.exists(temp_dir):
|
|
os.makedirs(temp_dir)
|
|
|
|
incremental_serialized_file = temp_dir + '/' + test_file_name + '.' \
|
|
+ test_case + '.postViaIncr.json'
|
|
post_edit_serialized_file = temp_dir + '/' + test_file_name + '.' \
|
|
+ test_case + '.post.json'
|
|
|
|
incremental_diags_file = temp_dir + '/' + test_file_name + '.' \
|
|
+ test_case + '.diagsViaIncr.txt'
|
|
post_edit_diags_file = temp_dir + '/' + test_file_name + '.' \
|
|
+ test_case + '.post.diags.txt'
|
|
|
|
# Generate the syntax tree once incrementally and once from scratch
|
|
try:
|
|
serializeIncrParseMarkupFile(test_file=test_file,
|
|
test_case=test_case,
|
|
mode='incremental',
|
|
serialization_mode='full',
|
|
omit_node_ids=True,
|
|
output_file=incremental_serialized_file,
|
|
diags_output_file=incremental_diags_file,
|
|
temp_dir=temp_dir + '/temp',
|
|
swift_syntax_test=swift_syntax_test,
|
|
print_visual_reuse_info=visual_reuse_info)
|
|
if visual_reuse_info:
|
|
# If we just want the reuse info, we don't need to parse the file
|
|
# from scratch or validate it
|
|
sys.exit(0)
|
|
|
|
serializeIncrParseMarkupFile(test_file=test_file,
|
|
test_case=test_case,
|
|
mode='post-edit',
|
|
serialization_mode='full',
|
|
omit_node_ids=True,
|
|
output_file=post_edit_serialized_file,
|
|
diags_output_file=post_edit_diags_file,
|
|
temp_dir=temp_dir + '/temp',
|
|
swift_syntax_test=swift_syntax_test,
|
|
print_visual_reuse_info=visual_reuse_info)
|
|
except TestFailedError as e:
|
|
print('Test case "%s" of %s FAILed' % (test_case, test_file),
|
|
file=sys.stderr)
|
|
print(e.message, file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# Check if the two syntax trees are the same
|
|
lines = difflib.unified_diff(io.open(incremental_serialized_file, 'r',
|
|
encoding='utf-8', errors='ignore').readlines(),
|
|
io.open(post_edit_serialized_file, 'r',
|
|
encoding='utf-8', errors='ignore').readlines(),
|
|
fromfile=incremental_serialized_file,
|
|
tofile=post_edit_serialized_file)
|
|
diff = '\n'.join(line for line in lines)
|
|
if diff:
|
|
print('Test case "%s" of %s FAILed' % (test_case, test_file),
|
|
file=sys.stderr)
|
|
print('Syntax tree of incremental parsing does not match '
|
|
'from-scratch parsing of post-edit file:\n\n', file=sys.stderr)
|
|
print(diff, file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# Verify that if the incremental parse resulted in parser diagnostics, those
|
|
# diagnostics were also emitted during the full parse.
|
|
# We can't just diff the outputs because the full parse includes diagnostics
|
|
# from the whole file, while the incremental parse includes only a subset.
|
|
# Each diagnostic is searched in the full parse diagnostics array but the
|
|
# search for each diagnostic continues from where the previous search
|
|
# stopped.
|
|
incremental_diags = open(incremental_diags_file).readlines()
|
|
post_edit_diags = open(post_edit_diags_file).readlines()
|
|
full_idx = 0
|
|
for diag in incremental_diags:
|
|
while full_idx < len(post_edit_diags):
|
|
if post_edit_diags[full_idx] == diag:
|
|
break
|
|
full_idx += 1
|
|
if full_idx == len(post_edit_diags):
|
|
print('Test case "%s" of %s FAILed' % (test_case, test_file),
|
|
file=sys.stderr)
|
|
print('Parser diagnostic of incremental parsing was not emitted '
|
|
'during from-scratch parsing of post-edit file:',
|
|
file=sys.stderr)
|
|
print(diag, file=sys.stderr)
|
|
sys.exit(1)
|
|
full_idx += 1 # continue searching from the next diagnostic line.
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|