Skip to content

Commit 0c3e4c6

Browse files
committed
add essential file drop from last 3 commits
1 parent b9cd2aa commit 0c3e4c6

File tree

1 file changed

+206
-0
lines changed

1 file changed

+206
-0
lines changed

explain_output_difference.py

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import os, re
2+
3+
# Expected output has been supplied as an environment variable
4+
# and the program has been stoped because the output was incorrect
5+
6+
def explain_output_difference(loc, output_stream, color):
7+
# autotest may try to print all errors in red, so disable this
8+
print(color('', 'black'), end='', file=output_stream)
9+
explain_output_difference1(loc, output_stream, color)
10+
print(file=output_stream)
11+
12+
def explain_output_difference1(loc, output_stream, color):
13+
14+
# values supplied for expected output for this execution
15+
expected_stdout = os.environb.get(b'DCC_EXPECTED_STDOUT', '')
16+
# ignore_case = getenv_boolean('DCC_IGNORE_CASE')
17+
# ignore_empty_lines = getenv_boolean('DCC_IGNORE_EMPTY_LINES')
18+
ignore_trailing_white_space = getenv_boolean('DCC_IGNORE_TRAILING_WHITE_SPACE', default=True)
19+
# compare_only_characters = os.environ.get('DCC_COMPARE_ONLY_CHARACTERS')
20+
21+
# value describing discrepency between actual and expected output
22+
reason = os.environ.get('DCC_OUTPUT_ERROR', '')
23+
line_number = getenv_int('DCC_ACTUAL_LINE_NUMBER')
24+
25+
# these values do not include current line
26+
n_expected_bytes_seen = getenv_int('DCC_N_EXPECTED_BYTES_SEEN')
27+
n_actual_bytes_seen = getenv_int('DCC_N_ACTUAL_BYTES_SEEN')
28+
29+
expected_line = os.environb.get(b'DCC_EXPECTED_LINE', '')
30+
actual_line = os.environb.get(b'DCC_ACTUAL_LINE', '')
31+
expected_column = getenv_int('DCC_EXPECTED_COLUMN')
32+
actual_column = getenv_int('DCC_ACTUAL_COLUMN')
33+
34+
if 0 <= actual_column < len(actual_line):
35+
actual_char = actual_line[actual_column]
36+
else:
37+
actual_char = None
38+
39+
if ignore_trailing_white_space and expected_stdout[-1:] == b'\n':
40+
expected_stdout = expected_stdout.rstrip() + b'\n'
41+
42+
n_expected_lines = len(expected_stdout.splitlines())
43+
is_last_expected_line = n_expected_bytes_seen + len(expected_line) >= len(expected_stdout)
44+
45+
if 0 <= expected_column < len(expected_line):
46+
expected_byte = expected_line[expected_column:expected_column+1]
47+
else:
48+
expected_byte = None
49+
50+
# primary = lambda text, **kwargs: color(text, style='bold', **kwargs)
51+
danger = lambda text, **kwargs: color(text, fg='red', style='bold', **kwargs)
52+
success = lambda text, **kwargs: color(text, fg='green', style='bold', **kwargs)
53+
# info = lambda text, **kwargs: color(text, fg='cyan', style='bold', **kwargs)
54+
55+
if not actual_line.endswith(b"\n"):
56+
print("Execution failed because ", end='', file=output_stream)
57+
else:
58+
print("Execution stopped because ", end='', file=output_stream)
59+
60+
if reason == "expected line too long":
61+
print("internal error: expected line too long", file=output_stream)
62+
return
63+
64+
if reason == "line too long":
65+
print("program wrote", danger("a line containing over " + str(actual_column) + " bytes."), file=output_stream)
66+
print("Do you have an infinite loop?", file=output_stream)
67+
print_line(actual_line, "start of the", danger, output_stream)
68+
return
69+
70+
if reason == "too much output":
71+
print("program produced", danger("too much output."), file=output_stream)
72+
print("Your program printed", actual_column, "bytes.")
73+
print("Do you have an infinite loop?", file=output_stream)
74+
print_line(actual_line, "last", danger, output_stream)
75+
return
76+
77+
if reason == "zero byte":
78+
print("a", danger("zero byte ('\\0')"), "was printed.", file=output_stream)
79+
print("Byte", actual_column, "of line", line_number, "of program's output was a zero byte ('\\0')", file=output_stream)
80+
if len(expected_line):
81+
print("Here are the characters on the line before the zero byte:", file=output_stream)
82+
print(sanitize(actual_line), file=output_stream)
83+
print("\nFor more information go to:", explanation_url("zero_byte"), file=output_stream)
84+
return
85+
86+
if n_actual_bytes_seen == 0 and len(actual_line) == 0:
87+
print("program produced", danger("no output."), file=output_stream)
88+
print(n_expected_lines, "lines of output were expected", file=output_stream)
89+
print_line(expected_line, "first expected", success, output_stream)
90+
return
91+
92+
if len(actual_line) == 0:
93+
print("of", danger("missing output lines."), file=output_stream)
94+
print("Your program printed", line_number, "lines of correct output but stopped before printing all the expected output.", file=output_stream)
95+
print_line(expected_line, "next expected", success, output_stream)
96+
return
97+
98+
if n_expected_bytes_seen == 0 and len(expected_line) == 0:
99+
print("program produced", danger("output when no output was expected."), file=output_stream)
100+
print_line(actual_line, "unexpected", danger, output_stream)
101+
return
102+
103+
if len(expected_line) == 0:
104+
print("of", danger("unexpected extra output."), file=output_stream)
105+
print("The program produced all the expected output and then produced extra output.", file=output_stream)
106+
print_line(actual_line, "extra", danger, output_stream)
107+
return
108+
109+
if is_last_expected_line and expected_byte == b'\n' and actual_char is None:
110+
print('the last', danger("newline was missing."), file=output_stream)
111+
print("Your program produced all the expected output, except the last newline ('\\n') was missing.", file=output_stream)
112+
print("\nFor more information go to", explanation_url("missing_newline"), file=output_stream)
113+
return
114+
115+
show_line_length = max(len(expected_line) + 8, 80)
116+
117+
118+
bad_characters_explanation = check_bad_characters(actual_line, line_number, danger, expected_line)
119+
if bad_characters_explanation:
120+
print(bad_characters_explanation, end='', file=output_stream)
121+
return
122+
123+
print("of an", danger('incorrect output line.'), file=output_stream)
124+
print('Byte', actual_column + 1, 'of line', line_number, "of program output was incorrect.", file=output_stream)
125+
126+
if not actual_line[actual_column+1:]:
127+
if actual_line.rstrip(b'\n') + expected_byte == expected_line.rstrip(b'\n'):
128+
print("A", "'" + danger(sanitize(expected_byte)) + "'", "was missing from the end of the output line.", file=output_stream)
129+
else:
130+
print("The characters you printed were correct, but more characters were expected.", file=output_stream)
131+
132+
print("The correct output line was:", file=output_stream)
133+
print(success(sanitize(expected_line, max_line_length_shown=show_line_length)), file=output_stream)
134+
135+
print("Your program printed this line:", file=output_stream)
136+
137+
correct_prefix = success(sanitize(actual_line[0:actual_column]))
138+
print(correct_prefix, end='', file=output_stream)
139+
140+
incorrect_byte = actual_line[actual_column:actual_column + 1]
141+
if incorrect_byte == ' ':
142+
print(danger(sanitize(incorrect_byte), bg='red'), end='', file=output_stream)
143+
else:
144+
print(danger(sanitize(incorrect_byte)), end='', file=output_stream)
145+
146+
print(sanitize(actual_line[actual_column+1:], max_line_length_shown=show_line_length - actual_column), file=output_stream)
147+
148+
def print_line(line, description, line_color, output_stream):
149+
if line == b'\n':
150+
print("The", description, "line was an empty line (a '\\n').", file=output_stream)
151+
else:
152+
print("The", description, "line was:", file=output_stream)
153+
print(line_color(sanitize(line)), file=output_stream)
154+
155+
156+
def sanitize(line, max_line_length_shown=256):
157+
if len(line) > max_line_length_shown:
158+
line = line[0:max_line_length_shown] + b' ...'
159+
return repr(line.rstrip(b'\n'))[2:-1]
160+
161+
162+
def check_bad_characters(line, line_number, danger, expected):
163+
if re.search(rb'[\x00-\x08\x14-\x1f\x7f-\xff]', expected):
164+
return None
165+
m = re.search(rb'^(.*?)([\x00-\x08\x14-\x1f\x7f-\xff])', line)
166+
if not m:
167+
return None
168+
(prefix, offending_char) = m.groups()
169+
offending_value = ord(offending_char)
170+
if offending_value == 0:
171+
description = "zero byte ('" + danger('\\0') + "')"
172+
elif offending_value > 127:
173+
description = "non-ascii byte " + danger("\\x%02x" % (offending_value))
174+
else:
175+
description = "non-printable character " + danger("\\x%02x" % (offending_value))
176+
column = len(prefix)
177+
explanation = "a " + danger("non-ASCII byte") + " was printed.\n"
178+
explanation += "Byte %d of line %d of program output was a %s\n" % (column + 1, line_number, description)
179+
180+
explanation += "Here is line %d with non-printable characters replaced with backslash-escaped equivalents:\n" % (line_number)
181+
line = repr(line)[2:-1] + '\n'
182+
line = re.sub(r'(\\x[0-9a-f][0-9a-f])', danger(r'\1'), line)
183+
explanation += line
184+
if offending_value == 255:
185+
explanation += "\nHave you accidentally printed the special EOF value getchar returns?\n"
186+
explanation += "For more information go to: " + explanation_url("eof_byte") + "\n"
187+
return explanation
188+
189+
def getenv_boolean(name, default=False):
190+
if name in os.environ:
191+
value = os.environ[name]
192+
return value and value[0] not in "0fFnN"
193+
else:
194+
return default
195+
196+
197+
def getenv_int(name):
198+
try:
199+
return int(os.environ.get(name, 0))
200+
except ValueError:
201+
return 0
202+
203+
EXPLANATION_BASE_URL = "https://comp1511unsw.github.io/dcc/"
204+
205+
def explanation_url(page):
206+
return EXPLANATION_BASE_URL + page + ".html"

0 commit comments

Comments
 (0)