-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgorpretest.py
276 lines (235 loc) · 8.95 KB
/
gorpretest.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# Gorpretest: A nutty taste test of running software.
# Copyright (C) 2024 Foxie EdianiaK a.k.a. F_TEK
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import subprocess
from time import time
from sys import argv, platform
from pathlib import Path
from difflib import Differ
# Inititate the dividor constant
DIV = "- ~ - ~ - ~ - ~ - ~ -"
# PRE-RUN CHECKS #
if len(argv) == 1:
# If no arguments were given, display help
print("gorpretest <COMPILED C> <TEST DIRECTORY>"
+ " <NUMBER START> <NUMBER END>"
+ " [INSTRUCT SUFFIX] [ASSUME SUFFIX]")
elif argv[1] in ["-h", "--help"]:
# Allow for displaying full help
print("gorpretest <COMPILED C> <TEST DIRECTORY>"
+ " <NUMBER START> <NUMBER END>"
+ " [INSTRUCT SUFFIX] [ASSUME SUFFIX]")
print("""
<>/* = required arguments
COMPILED C*
or any other executable program
TEST DIRECTORY*
a directory to pull the inputs and expected outputs from
NUMBER START*
the number the tests should start at
NUMBER END*
the number the tests should end at
INSTRUCT SUFFIX
you can enter your own or it defaults to
"_in.txt" (no matter the platform)
ASSUME SUFFIX
you can enter your own or it defaults to
"_out_win.txt" on Windows
"_out.txt" on Linux
# FILE NAME FORMAT
<TEST DIRECTORY>/<NUMBER ZERO-PADDDED TO FOUR><CHOSEN SUFFIX>
e.g. gorpretest.py a.exe sample 1 11
sample/0001_in.txt -- sample/0011_out_win.txt""")
elif argv[1] in ["-v", "--version"]:
# Allow for displaying the current version and license info
print("Gorpretest [1.0.0] - A nutty taste test of running software.")
print()
print("""Copyright (C) 2024 Foxie EdianiaK a.k.a. F_TEK
This program comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
For more details refer to the LICENSE file in the GitHub repository.""")
elif len(argv) < 5:
# Check the minimum amount of required arguments.
print("\nErr: Wrong amount of arguments.")
else:
# Display license info
print("Gorpretest [1.0.0] - A nutty taste test of running software.")
print()
print("""Copyright (C) 2024 Foxie EdianiaK a.k.a. F_TEK
This program comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
For more details refer to the LICENSE file in the GitHub repository.""")
# Initiate the error monitor
check = True
# GIVEN ARGUMENT/INPUT CHECK #
"""
Executable Program Check
- Check if the given path:
- is valid.
- is a file.
- is runnable from the current platfrom.
(.exe for Windows, not .exe for Linux)
"""
PROGRAM = Path(argv[1])
PROGRAM_ABS = PROGRAM.absolute()
if not PROGRAM.is_file():
print(f"\nErr: Entered program ({PROGRAM}) is not a file.")
check = False
elif (PROGRAM.suffix != ".exe" and "win" in platform):
print(f"\nErr: This program ({PROGRAM}) isn't runnable"
+ " from a Windows console.")
check = False
elif (PROGRAM.suffix == ".exe" and "linux" in platform):
print(f"\nErr: This program ({PROGRAM}) isn't runnable"
+ " from a Bash-like console.")
check = False
"""
Sample Directory Check
- Check if the given path:
- is valid.
- is a directory.
"""
TEST_DIRECTORY = Path(argv[2])
if not TEST_DIRECTORY.is_dir():
print(f"\nErr: Entered path ({TEST_DIRECTORY}) is not a directory.")
check = False
"""
File Range Check
1. Check if entered numbers are valid.
2. Add custom suffixes or uses default platform-dependant ones.
3. Check if the first "ins" and last "as" file could be found.
"""
TEST_INT_START = argv[3]
try:
TEST_INT_START = int(argv[3])
except ValueError:
print(f"\nErr: The entered start number ({TEST_INT_START})"
+ " is not a number.")
check = False
TEST_INT_END = argv[4]
try:
TEST_INT_END = int(argv[4])
except ValueError:
print(f"\nErr: The entered end number ({TEST_INT_END}) is not a number.")
check = False
if len(argv) == 7:
TEST_INS_SUF = argv[5]
TEST_AS_SUF = argv[6]
else:
TEST_INS_SUF = "_in.txt"
if "win" in platform:
TEST_AS_SUF = "_out_win.txt"
elif "linux" in platform:
TEST_AS_SUF = "_out.txt"
try:
with open(f"{TEST_DIRECTORY}/"
+ f"{TEST_INT_START:04}{TEST_INS_SUF}", "r") as f:
f.close()
except FileNotFoundError:
print("\nErr: Couldn't find file "
+ f"{TEST_INT_START:04}{TEST_INS_SUF}"
+ f" in /{str(TEST_DIRECTORY)}.")
check = False
try:
with open(f"{TEST_DIRECTORY}/"
+ f"{TEST_INT_END:04}{TEST_AS_SUF}", "r") as f:
f.close()
except FileNotFoundError:
print("\nErr: Couldn't find file "
+ f"{TEST_INT_END:04}{TEST_AS_SUF}"
+ f" in /{str(TEST_DIRECTORY)}.")
check = False
"""
Manual User Check
- List all the entered information.
- Last stop before the program starts all the tests.
"""
if check:
print("\n" * 50)
print("PRE-RUN CHECKS complete!")
print(f"About to run {str(PROGRAM)}"
+ f" with files ({TEST_INT_START:04}{TEST_INS_SUF}"
+ f" // {TEST_INT_END:04}{TEST_AS_SUF})"
+ f" from /{TEST_DIRECTORY}.")
fin = input("Continue? [Y/n] ")
if fin.upper() == "N":
check = False
print("Aww, goodbye then. ;C")
else:
print("Yum, yum~ >o<")
if check:
print(DIV)
# Initialize variables for statistics
tests = 0
times = []
for i in range(TEST_INT_START, TEST_INT_END + 1):
# Load current test's file paths
instruct = f"{TEST_DIRECTORY}/{i:04}{TEST_INS_SUF}"
assume = f"{TEST_DIRECTORY}/{i:04}{TEST_AS_SUF}"
# Actually run the given program
with open(instruct, "r") as f:
print(f"\n>> Running TEST #{i}...")
# Save start time, used for time calculations
time_start = time()
r = subprocess.run([PROGRAM_ABS],
stdin=f,
capture_output=True).stdout
# Calculate the time the program took to run
time_end = time()
time_done = time_end - time_start
f.close()
"""
Compare with expected results.
- If they match:
- add time to average list.
- tell the user that all is well.
- If they do NOT match:
- show the user a diff between
the given output and expected output.
- tell the user.
"""
with open(assume, "rb") as f:
# Load current test's assumed output
test = f.read()
# Rounded time for later messages
time_done_round = round(time_done, 3)
if r != test:
# Decoded bytes for Differ
r_raw = r.decode().splitlines(True)
test_raw = test.decode().splitlines(True)
print(''.join(Differ().compare(r_raw, test_raw)), end="")
input(f">> TEST {i} ({time_done_round}s) failed!")
else:
print(f">> TEST #{i} done in {time_done_round}s!")
times.append(time_done)
tests += 1
f.close()
# Calculate time statistics
time_avg = round((sum(times) / len(times)), 6)
time_max = round(max(times), 6)
# Calculate number of tests
total_tests = len(range(TEST_INT_START, TEST_INT_END + 1))
"""
Final Report
- Which consists of:
- the dividor.
- number of successfully finished tests.
- total number of ran tests.
- statistics, such as:
- the average runtime.
- the longest runtime of all successful tests.
"""
print("\n" + DIV)
print(f"{tests}/{total_tests} TESTS passed!")
print("\nRUNTIME STATISTICS")
print(f"Average: {time_avg} seconds")
print(f"Max: {time_max} seconds")