| ... |
... |
@@ -1831,7 +1831,8 @@ async def do_test(name: TestName, |
|
1831
|
1831
|
reason = '%s (fragile)' % result.reason if result.reason else 'fragile'
|
|
1832
|
1832
|
t.fragile_failures.append(TestResult(directory, name, reason, way,
|
|
1833
|
1833
|
stdout=result.stdout,
|
|
1834
|
|
- stderr=result.stderr))
|
|
|
1834
|
+ stderr=result.stderr,
|
|
|
1835
|
+ diff=result.diff))
|
|
1835
|
1836
|
elif result.passed:
|
|
1836
|
1837
|
if _expect_pass(way):
|
|
1837
|
1838
|
t.expected_passes.append(TestResult(directory, name, "", way))
|
| ... |
... |
@@ -1850,7 +1851,8 @@ async def do_test(name: TestName, |
|
1850
|
1851
|
if_verbose(1, '*** unexpected failure for %s' % full_name)
|
|
1851
|
1852
|
tr = TestResult(directory, name, reason, way,
|
|
1852
|
1853
|
stdout=result.stdout,
|
|
1853
|
|
- stderr=result.stderr)
|
|
|
1854
|
+ stderr=result.stderr,
|
|
|
1855
|
+ diff=result.diff)
|
|
1854
|
1856
|
t.unexpected_failures.append(tr)
|
|
1855
|
1857
|
else:
|
|
1856
|
1858
|
t.n_expected_failures += 1
|
| ... |
... |
@@ -2022,19 +2024,16 @@ async def do_compile(name: TestName, |
|
2022
|
2024
|
|
|
2023
|
2025
|
expected_stderr_file = find_expected_file(name, 'stderr', way)
|
|
2024
|
2026
|
actual_stderr_file = add_suffix(name, 'comp.stderr')
|
|
2025
|
|
- diff_file_name = in_testdir(add_suffix(name, 'comp.diff'))
|
|
2026
|
|
-
|
|
2027
|
|
- if compare_stderr and not await compare_outputs(way, 'stderr',
|
|
|
2027
|
+ if compare_stderr:
|
|
|
2028
|
+ stderr_match = await compare_outputs(way, 'stderr',
|
|
2028
|
2029
|
join_normalisers(getTestOpts().extra_errmsg_normaliser,
|
|
2029
|
2030
|
normalise_errmsg),
|
|
2030
|
2031
|
expected_stderr_file, actual_stderr_file,
|
|
2031
|
|
- diff_file=diff_file_name,
|
|
2032
|
2032
|
whitespace_normaliser=getattr(getTestOpts(),
|
|
2033
|
2033
|
"whitespace_normaliser",
|
|
2034
|
|
- normalise_whitespace)):
|
|
2035
|
|
- stderr = diff_file_name.read_text()
|
|
2036
|
|
- diff_file_name.unlink()
|
|
2037
|
|
- return failBecause('stderr mismatch', stderr=stderr)
|
|
|
2034
|
+ normalise_whitespace))
|
|
|
2035
|
+ if not stderr_match:
|
|
|
2036
|
+ return failBecause('stderr mismatch', diff=stderr_match.diff)
|
|
2038
|
2037
|
|
|
2039
|
2038
|
opts = getTestOpts()
|
|
2040
|
2039
|
if isGenericStatsTest():
|
| ... |
... |
@@ -2064,10 +2063,11 @@ async def compile_cmp_asm(name: TestName, |
|
2064
|
2063
|
expected_asm_file = find_expected_file(name, 'asm', way)
|
|
2065
|
2064
|
actual_asm_file = add_suffix(name, 's')
|
|
2066
|
2065
|
|
|
2067
|
|
- if not await compare_outputs(way, 'asm',
|
|
|
2066
|
+ asm_match = await compare_outputs(way, 'asm',
|
|
2068
|
2067
|
join_normalisers(normalise_errmsg, normalise_asm),
|
|
2069
|
|
- expected_asm_file, actual_asm_file):
|
|
2070
|
|
- return failBecause('asm mismatch')
|
|
|
2068
|
+ expected_asm_file, actual_asm_file)
|
|
|
2069
|
+ if not asm_match:
|
|
|
2070
|
+ return failBecause('asm mismatch', diff=asm_match.diff)
|
|
2071
|
2071
|
|
|
2072
|
2072
|
# no problems found, this test passed
|
|
2073
|
2073
|
return passed()
|
| ... |
... |
@@ -2148,19 +2148,15 @@ async def compile_and_run__(name: TestName, |
|
2148
|
2148
|
if compile_stderr:
|
|
2149
|
2149
|
expected_stderr_file = find_expected_file(name, 'ghc.stderr', way)
|
|
2150
|
2150
|
actual_stderr_file = add_suffix(name, 'comp.stderr')
|
|
2151
|
|
- diff_file_name = in_testdir(add_suffix(name, 'comp.diff'))
|
|
2152
|
|
-
|
|
2153
|
|
- if not await compare_outputs(way, 'stderr',
|
|
|
2151
|
+ ghc_stderr_match = await compare_outputs(way, 'stderr',
|
|
2154
|
2152
|
join_normalisers(getTestOpts().extra_errmsg_normaliser,
|
|
2155
|
2153
|
normalise_errmsg),
|
|
2156
|
2154
|
expected_stderr_file, actual_stderr_file,
|
|
2157
|
|
- diff_file=diff_file_name,
|
|
2158
|
2155
|
whitespace_normaliser=getattr(getTestOpts(),
|
|
2159
|
2156
|
"whitespace_normaliser",
|
|
2160
|
|
- normalise_whitespace)):
|
|
2161
|
|
- stderr = diff_file_name.read_text()
|
|
2162
|
|
- diff_file_name.unlink()
|
|
2163
|
|
- return failBecause('ghc.stderr mismatch', stderr=stderr)
|
|
|
2157
|
+ normalise_whitespace))
|
|
|
2158
|
+ if not ghc_stderr_match:
|
|
|
2159
|
+ return failBecause('ghc.stderr mismatch', diff=ghc_stderr_match.diff)
|
|
2164
|
2160
|
|
|
2165
|
2161
|
opts = getTestOpts()
|
|
2166
|
2162
|
extension = exe_extension() if not opts.ignore_extension else ""
|
| ... |
... |
@@ -2449,14 +2445,18 @@ async def simple_run(name: TestName, way: WayName, prog: str, extra_run_opts: st |
|
2449
|
2445
|
message = format_bad_exit_code_message(exit_code)
|
|
2450
|
2446
|
return failBecause(message)
|
|
2451
|
2447
|
|
|
2452
|
|
- if not (opts.ignore_stderr or await stderr_ok(name, way) or opts.combined_output):
|
|
|
2448
|
+ stderr_match = CompareOutput(True) if (opts.ignore_stderr or opts.combined_output) else await stderr_ok(name, way)
|
|
|
2449
|
+ if not stderr_match:
|
|
2453
|
2450
|
return failBecause('bad stderr',
|
|
2454
|
2451
|
stderr=read_stderr(name),
|
|
2455
|
|
- stdout=read_stdout(name))
|
|
2456
|
|
- if not (opts.ignore_stdout or await stdout_ok(name, way)):
|
|
|
2452
|
+ stdout=read_stdout(name),
|
|
|
2453
|
+ diff=stderr_match.diff)
|
|
|
2454
|
+ stdout_match = CompareOutput(True) if opts.ignore_stdout else await stdout_ok(name, way)
|
|
|
2455
|
+ if not stdout_match:
|
|
2457
|
2456
|
return failBecause('bad stdout',
|
|
2458
|
2457
|
stderr=read_stderr(name),
|
|
2459
|
|
- stdout=read_stdout(name))
|
|
|
2458
|
+ stdout=read_stdout(name),
|
|
|
2459
|
+ diff=stdout_match.diff)
|
|
2460
|
2460
|
|
|
2461
|
2461
|
check_hp = '-hT' in my_rts_flags and opts.check_hp
|
|
2462
|
2462
|
check_prof = '-p' in my_rts_flags
|
| ... |
... |
@@ -2464,8 +2464,10 @@ async def simple_run(name: TestName, way: WayName, prog: str, extra_run_opts: st |
|
2464
|
2464
|
# exit_code > 127 probably indicates a crash, so don't try to run hp2ps.
|
|
2465
|
2465
|
if check_hp and (exit_code <= 127 or exit_code == 251) and not await check_hp_ok(name):
|
|
2466
|
2466
|
return failBecause('bad heap profile')
|
|
2467
|
|
- if check_prof and not await check_prof_ok(name, way):
|
|
2468
|
|
- return failBecause('bad profile')
|
|
|
2467
|
+ if check_prof:
|
|
|
2468
|
+ prof_match = await check_prof_ok(name, way)
|
|
|
2469
|
+ if not prof_match:
|
|
|
2470
|
+ return failBecause('bad profile', diff=prof_match.diff)
|
|
2469
|
2471
|
|
|
2470
|
2472
|
# Check the results of stats tests
|
|
2471
|
2473
|
if isGenericStatsTest():
|
| ... |
... |
@@ -2559,20 +2561,23 @@ async def interpreter_run(name: TestName, |
|
2559
|
2561
|
|
|
2560
|
2562
|
# ToDo: if the sub-shell was killed by ^C, then exit
|
|
2561
|
2563
|
|
|
2562
|
|
- if not (opts.ignore_stderr or await stderr_ok(name, way)):
|
|
|
2564
|
+ stderr_match = CompareOutput(True) if opts.ignore_stderr else await stderr_ok(name, way)
|
|
|
2565
|
+ if not stderr_match:
|
|
2563
|
2566
|
if _expect_pass(way):
|
|
2564
|
2567
|
dump_stderr_for('comp', name)
|
|
2565
|
2568
|
return failBecause('bad stderr',
|
|
2566
|
2569
|
stderr=read_stderr(name),
|
|
2567
|
|
- stdout=read_stdout(name))
|
|
2568
|
|
- elif not (opts.ignore_stdout or await stdout_ok(name, way)):
|
|
|
2570
|
+ stdout=read_stdout(name),
|
|
|
2571
|
+ diff=stderr_match.diff)
|
|
|
2572
|
+ stdout_match = CompareOutput(True) if opts.ignore_stdout else await stdout_ok(name, way)
|
|
|
2573
|
+ if not stdout_match:
|
|
2569
|
2574
|
if _expect_pass(way):
|
|
2570
|
2575
|
dump_stderr_for('comp', name)
|
|
2571
|
2576
|
return failBecause('bad stdout',
|
|
2572
|
2577
|
stderr=read_stderr(name),
|
|
2573
|
|
- stdout=read_stdout(name))
|
|
2574
|
|
- else:
|
|
2575
|
|
- return passed()
|
|
|
2578
|
+ stdout=read_stdout(name),
|
|
|
2579
|
+ diff=stdout_match.diff)
|
|
|
2580
|
+ return passed()
|
|
2576
|
2581
|
|
|
2577
|
2582
|
def split_file(in_fn: Path, delimiter: str, out1_fn: Path, out2_fn: Path):
|
|
2578
|
2583
|
# See Note [Universal newlines].
|
| ... |
... |
@@ -2603,7 +2608,15 @@ def get_compiler_flags() -> List[str]: |
|
2603
|
2608
|
|
|
2604
|
2609
|
return flags
|
|
2605
|
2610
|
|
|
2606
|
|
-async def stdout_ok(name: TestName, way: WayName) -> bool:
|
|
|
2611
|
+class CompareOutput:
|
|
|
2612
|
+ __slots__ = 'ok', 'diff'
|
|
|
2613
|
+ def __init__(self, ok: bool, diff: Optional[str]=None) -> None:
|
|
|
2614
|
+ self.ok = ok
|
|
|
2615
|
+ self.diff = diff
|
|
|
2616
|
+ def __bool__(self) -> bool:
|
|
|
2617
|
+ return self.ok
|
|
|
2618
|
+
|
|
|
2619
|
+async def stdout_ok(name: TestName, way: WayName) -> CompareOutput:
|
|
2607
|
2620
|
actual_stdout_file = add_suffix(name, 'run.stdout')
|
|
2608
|
2621
|
expected_stdout_file = find_expected_file(name, 'stdout', way)
|
|
2609
|
2622
|
|
| ... |
... |
@@ -2612,7 +2625,7 @@ async def stdout_ok(name: TestName, way: WayName) -> bool: |
|
2612
|
2625
|
check_stdout = getTestOpts().check_stdout
|
|
2613
|
2626
|
if check_stdout is not None:
|
|
2614
|
2627
|
actual_stdout_path = in_testdir(actual_stdout_file)
|
|
2615
|
|
- return check_stdout(actual_stdout_path, extra_norm)
|
|
|
2628
|
+ return CompareOutput(check_stdout(actual_stdout_path, extra_norm))
|
|
2616
|
2629
|
|
|
2617
|
2630
|
return await compare_outputs(way, 'stdout', extra_norm,
|
|
2618
|
2631
|
expected_stdout_file, actual_stdout_file)
|
| ... |
... |
@@ -2624,13 +2637,21 @@ def read_stdout( name: TestName ) -> str: |
|
2624
|
2637
|
else:
|
|
2625
|
2638
|
return ''
|
|
2626
|
2639
|
|
|
|
2640
|
+def read_diff( diff_file: Path ) -> Optional[str]:
|
|
|
2641
|
+ if diff_file.exists():
|
|
|
2642
|
+ diff = diff_file.read_text()
|
|
|
2643
|
+ diff_file.unlink()
|
|
|
2644
|
+ return diff or None
|
|
|
2645
|
+ else:
|
|
|
2646
|
+ return None
|
|
|
2647
|
+
|
|
2627
|
2648
|
def dump_stdout( name: TestName ) -> None:
|
|
2628
|
2649
|
s = read_stdout(name).strip()
|
|
2629
|
2650
|
if s:
|
|
2630
|
2651
|
print("Stdout (", name, "):")
|
|
2631
|
2652
|
safe_print(s)
|
|
2632
|
2653
|
|
|
2633
|
|
-async def stderr_ok(name: TestName, way: WayName) -> bool:
|
|
|
2654
|
+async def stderr_ok(name: TestName, way: WayName) -> CompareOutput:
|
|
2634
|
2655
|
actual_stderr_file = add_suffix(name, 'run.stderr')
|
|
2635
|
2656
|
expected_stderr_file = find_expected_file(name, 'stderr', way)
|
|
2636
|
2657
|
|
| ... |
... |
@@ -2736,48 +2757,48 @@ async def check_hp_ok(name: TestName) -> bool: |
|
2736
|
2757
|
print("hp2ps error when processing heap profile for " + name)
|
|
2737
|
2758
|
return False
|
|
2738
|
2759
|
|
|
2739
|
|
-async def check_prof_ok(name: TestName, way: WayName) -> bool:
|
|
|
2760
|
+async def check_prof_ok(name: TestName, way: WayName) -> CompareOutput:
|
|
2740
|
2761
|
expected_prof_file = find_expected_file(name, 'prof.sample', way)
|
|
2741
|
2762
|
expected_prof_path = in_testdir(expected_prof_file)
|
|
2742
|
2763
|
|
|
2743
|
2764
|
# Check actual prof file only if we have an expected prof file to
|
|
2744
|
2765
|
# compare it with.
|
|
2745
|
2766
|
if not expected_prof_path.exists():
|
|
2746
|
|
- return True
|
|
|
2767
|
+ return CompareOutput(True)
|
|
2747
|
2768
|
|
|
2748
|
2769
|
actual_prof_file = add_suffix(name, 'prof')
|
|
2749
|
2770
|
actual_prof_path = in_testdir(actual_prof_file)
|
|
2750
|
2771
|
|
|
2751
|
2772
|
if not actual_prof_path.exists():
|
|
2752
|
2773
|
print("%s does not exist" % actual_prof_path)
|
|
2753
|
|
- return(False)
|
|
|
2774
|
+ return CompareOutput(False)
|
|
2754
|
2775
|
|
|
2755
|
2776
|
if actual_prof_path.stat().st_size == 0:
|
|
2756
|
2777
|
print("%s is empty" % actual_prof_path)
|
|
2757
|
|
- return(False)
|
|
|
2778
|
+ return CompareOutput(False)
|
|
2758
|
2779
|
|
|
2759
|
2780
|
return await compare_outputs(way, 'prof', normalise_prof,
|
|
2760
|
2781
|
expected_prof_file, actual_prof_file,
|
|
2761
|
2782
|
whitespace_normaliser=normalise_whitespace)
|
|
2762
|
2783
|
|
|
2763
|
2784
|
# Compare expected output to actual output, and optionally accept the
|
|
2764
|
|
-# new output. Returns true if output matched or was accepted, false
|
|
2765
|
|
-# otherwise. See Note [Output comparison] for the meaning of the
|
|
2766
|
|
-# normaliser and whitespace_normaliser parameters.
|
|
|
2785
|
+# new output. Returns a truthy CompareOutput if output matched or was
|
|
|
2786
|
+# accepted, a falsy one (with the diff) otherwise. See Note [Output
|
|
|
2787
|
+# comparison] for the meaning of the normaliser and whitespace_normaliser
|
|
|
2788
|
+# parameters.
|
|
2767
|
2789
|
async def compare_outputs(
|
|
2768
|
2790
|
way: WayName,
|
|
2769
|
2791
|
kind: str,
|
|
2770
|
2792
|
normaliser: OutputNormalizer,
|
|
2771
|
2793
|
expected_file: Path,
|
|
2772
|
2794
|
actual_file: Path,
|
|
2773
|
|
- diff_file: Optional[Path]=None,
|
|
2774
|
|
- whitespace_normaliser: OutputNormalizer=lambda x:x) -> bool:
|
|
|
2795
|
+ whitespace_normaliser: OutputNormalizer=lambda x:x) -> CompareOutput:
|
|
2775
|
2796
|
|
|
2776
|
2797
|
# Respect ignore_stdout and ignore_stderr options
|
|
2777
|
2798
|
if kind == 'stderr' and getTestOpts().ignore_stderr:
|
|
2778
|
|
- return True
|
|
|
2799
|
+ return CompareOutput(True)
|
|
2779
|
2800
|
if kind == 'stdout' and getTestOpts().ignore_stdout:
|
|
2780
|
|
- return True
|
|
|
2801
|
+ return CompareOutput(True)
|
|
2781
|
2802
|
|
|
2782
|
2803
|
expected_path = in_srcdir(expected_file)
|
|
2783
|
2804
|
actual_path = in_testdir(actual_file)
|
| ... |
... |
@@ -2797,7 +2818,7 @@ async def compare_outputs( |
|
2797
|
2818
|
|
|
2798
|
2819
|
# See Note [Output comparison].
|
|
2799
|
2820
|
if whitespace_normaliser(expected_str) == whitespace_normaliser(actual_str):
|
|
2800
|
|
- return True
|
|
|
2821
|
+ return CompareOutput(True)
|
|
2801
|
2822
|
else:
|
|
2802
|
2823
|
if config.verbose >= 1 and _expect_pass(way):
|
|
2803
|
2824
|
print('Actual ' + kind + ' output differs from expected:')
|
| ... |
... |
@@ -2809,6 +2830,7 @@ async def compare_outputs( |
|
2809
|
2830
|
actual_normalised_path = add_suffix(actual_path, 'normalised')
|
|
2810
|
2831
|
write_file(actual_normalised_path, actual_str)
|
|
2811
|
2832
|
|
|
|
2833
|
+ diff_file = add_suffix(actual_path, 'diff')
|
|
2812
|
2834
|
if config.verbose >= 1 and _expect_pass(way):
|
|
2813
|
2835
|
# See Note [Output comparison].
|
|
2814
|
2836
|
r = await runCmd('diff -uw "{0}" "{1}"'.format(null2unix_null(expected_normalised_path),
|
| ... |
... |
@@ -2823,13 +2845,14 @@ async def compare_outputs( |
|
2823
|
2845
|
actual_normalised_path),
|
|
2824
|
2846
|
stdout=diff_file,
|
|
2825
|
2847
|
print_output=True)
|
|
2826
|
|
- elif diff_file: diff_file.open('ab').close() # Make sure the file exists still as
|
|
2827
|
|
- # we will try to read it later
|
|
|
2848
|
+ else:
|
|
|
2849
|
+ diff_file.open('ab').close() # Make sure the file exists still as
|
|
|
2850
|
+ # we will try to read it later
|
|
2828
|
2851
|
|
|
2829
|
2852
|
if config.accept and (getTestOpts().expect == 'fail' or
|
|
2830
|
2853
|
way in getTestOpts().expect_fail_for):
|
|
2831
|
2854
|
if_verbose(1, 'Test is expected to fail. Not accepting new output.')
|
|
2832
|
|
- return False
|
|
|
2855
|
+ return CompareOutput(False, read_diff(diff_file))
|
|
2833
|
2856
|
elif config.accept and actual_raw:
|
|
2834
|
2857
|
if config.accept_platform:
|
|
2835
|
2858
|
if_verbose(1, 'Accepting new output for platform "'
|
| ... |
... |
@@ -2843,11 +2866,11 @@ async def compare_outputs( |
|
2843
|
2866
|
if_verbose(1, 'Accepting new output.')
|
|
2844
|
2867
|
|
|
2845
|
2868
|
write_file(expected_path, actual_raw)
|
|
2846
|
|
- return True
|
|
|
2869
|
+ return CompareOutput(True)
|
|
2847
|
2870
|
elif config.accept:
|
|
2848
|
2871
|
if_verbose(1, 'No output. Deleting "{0}".'.format(expected_path))
|
|
2849
|
2872
|
expected_path.unlink()
|
|
2850
|
|
- return True
|
|
|
2873
|
+ return CompareOutput(True)
|
|
2851
|
2874
|
else:
|
|
2852
|
2875
|
if config.unexpected_output_dir is not None:
|
|
2853
|
2876
|
ghc_root = expected_path.relative_to(config.top.parent)
|
| ... |
... |
@@ -2855,7 +2878,7 @@ async def compare_outputs( |
|
2855
|
2878
|
out.parent.mkdir(exist_ok=True, parents=True)
|
|
2856
|
2879
|
write_file(out, actual_raw)
|
|
2857
|
2880
|
|
|
2858
|
|
- return False
|
|
|
2881
|
+ return CompareOutput(False, read_diff(diff_file))
|
|
2859
|
2882
|
|
|
2860
|
2883
|
# Checks that each line from pattern_file is present in actual_file as
|
|
2861
|
2884
|
# a substring or regex pattern depending on is_substring.
|