Skip to content

Commit 3829958

Browse files
committed
Add method to run user's code against sample cases
1 parent ff3063b commit 3829958

File tree

3 files changed

+199
-6
lines changed

3 files changed

+199
-6
lines changed

ACedIt/main.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,17 @@ def validate_args(args):
99
if args["site"] not in supported_sites:
1010
print "Sorry. ACedIt only supports %s till now." % (", ".join(supported_sites))
1111
sys.exit(0)
12+
13+
if args["source"] and (args["contest"] or args["problem"] or args["force"]):
14+
print "ACedIt run <source_file> doesn't support other flags"
15+
sys.exit(0)
16+
elif args["source"]:
17+
return
18+
1219
if not args["site"] == "spoj" and args["contest"] is None:
1320
print "Please specify a contest code"
1421
sys.exit(0)
22+
1523
if args["site"] == "spoj" and args["problem"] is None:
1624
print "Please specify a problem code for Spoj"
1725
sys.exit(0)
@@ -20,19 +28,24 @@ def validate_args(args):
2028
def main():
2129
args = util.Utilities.parse_flags()
2230
validate_args(args)
31+
2332
try:
24-
if args["problem"] is not None:
33+
if args["source"]:
34+
# run code
35+
util.Utilities.run_solution(args["source"])
36+
37+
elif args["problem"] is not None:
2538
# fetch single problem
2639
util.Utilities.download_problem_testcases(args)
40+
2741
else:
2842
# fetch all problems for the contest
2943
util.Utilities.download_contest_testcases(args)
44+
3045
except KeyboardInterrupt:
3146
# Clean up files here
3247
print "Interruped manually. Exiting gracefully."
3348

34-
print args
35-
3649

3750
if __name__ == "__main__":
3851
main()

ACedIt/util.py

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
class Utilities:
2020

2121
cache_dir = os.path.join(os.path.expanduser("~"), ".cache", "ACedIt")
22+
colors = {
23+
'GREEN': '\033[92m',
24+
'YELLOW': '\033[93m',
25+
'RED': '\033[91m',
26+
'ENDC': '\033[0m',
27+
'BOLD': '\033[1m',
28+
}
2229

2330
@staticmethod
2431
def parse_flags():
@@ -45,6 +52,10 @@ def parse_flags():
4552
action="store_true",
4653
help="Force download the test cases, even if they are cached")
4754

55+
parser.add_argument("--run",
56+
dest="source_file",
57+
help="Name of source file to be run")
58+
4859
parser.set_defaults(force=False)
4960

5061
args = parser.parse_args()
@@ -55,7 +66,7 @@ def parse_flags():
5566
import json
5667
default_site = None
5768
try:
58-
with open("constants.json", "r") as f:
69+
with open(os.path.join(Utilities.cache_dir, "constants.json"), "r") as f:
5970
data = f.read()
6071
data = json.loads(data)
6172
default_site = data.get("default_site", None)
@@ -70,6 +81,7 @@ def parse_flags():
7081
flags["problem"] = args.problem
7182
flags["force"] = args.force
7283
flags["site"] = flags["site"].lower()
84+
flags["source"] = args.source_file
7385

7486
return flags
7587

@@ -111,6 +123,9 @@ def store_files(site, contest, problem, inputs, outputs):
111123

112124
@staticmethod
113125
def download_problem_testcases(args):
126+
"""
127+
Download test cases for a given problem
128+
"""
114129
if args["site"] == "codeforces":
115130
platform = Codeforces(args)
116131
elif args["site"] == "codechef":
@@ -131,6 +146,9 @@ def download_problem_testcases(args):
131146

132147
@staticmethod
133148
def download_contest_testcases(args):
149+
"""
150+
Download test cases for all problems in a given contest
151+
"""
134152
if args["site"] == "codeforces":
135153
platform = Codeforces(args)
136154
elif args["site"] == "codechef":
@@ -143,6 +161,168 @@ def download_contest_testcases(args):
143161

144162
platform.scrape_contest()
145163

164+
@staticmethod
165+
def input_file_to_string(path, num_cases):
166+
"""
167+
Method to return sample inputs as a list
168+
"""
169+
inputs = []
170+
171+
for i in xrange(num_cases):
172+
with open(os.path.join(path, 'Input' + str(i)), 'r') as fh:
173+
inputs += [fh.read()]
174+
175+
return inputs
176+
177+
@staticmethod
178+
def cleanup(num_cases):
179+
"""
180+
Method to clean up temporarily created files
181+
"""
182+
for i in xrange(num_cases):
183+
if os.path.isfile('temp_output' + str(i)):
184+
os.remove('temp_output' + str(i))
185+
186+
@staticmethod
187+
def run_solution(problem):
188+
"""
189+
Method to run and test the user's solution against sample cases
190+
"""
191+
extension = problem.split('.')[-1]
192+
problem = problem.split('.')[0]
193+
problem_path = os.path.join(os.getcwd(), problem)
194+
195+
if not os.path.isfile(problem_path + '.' + extension):
196+
print "ERROR : No such file"
197+
sys.exit(0)
198+
199+
testcases_path = os.path.join(
200+
Utilities.cache_dir, *problem_path.split('/')[-3:])
201+
202+
if os.path.isdir(testcases_path):
203+
num_cases = len(os.listdir(testcases_path)) / 2
204+
results, expected_outputs, user_outputs = [], [], []
205+
206+
if extension == 'py':
207+
208+
for i in xrange(num_cases):
209+
status = os.system('cat ' + os.path.join(testcases_path, 'Input' + str(
210+
i)) + ' | timeout 3s python ' + problem + '.py > temp_output' + str(i))
211+
if status == 124:
212+
# Time Limit Exceeded
213+
results += [Utilities.colors['BOLD'] +
214+
Utilities.colors['YELLOW'] + 'TLE' + Utilities.colors['ENDC']]
215+
216+
elif status == 0:
217+
218+
with open('temp_output' + str(i), 'r') as temp_handler, open(os.path.join(testcases_path, 'Output' + str(i)), 'r') as out_handler:
219+
expected_output = out_handler.read().strip()
220+
user_output = temp_handler.read().strip()
221+
222+
expected_outputs += [expected_output]
223+
user_outputs += [user_output]
224+
225+
if expected_output == user_output:
226+
# All Correct
227+
results += [Utilities.colors['BOLD'] + Utilities.colors[
228+
'GREEN'] + 'AC' + Utilities.colors['ENDC']]
229+
else:
230+
# Wrong Answer
231+
results += [Utilities.colors['BOLD'] +
232+
Utilities.colors['RED'] + 'WA' + Utilities.colors['ENDC']]
233+
234+
else:
235+
# Runtime Error
236+
results += [Utilities.colors['BOLD'] +
237+
Utilities.colors['RED'] + 'RTE' + Utilities.colors['ENDC']]
238+
239+
elif extension == 'cpp' or extension == 'c':
240+
241+
compiler = {'c': 'gcc', 'cpp': 'g++'}[extension]
242+
compile_status = os.system(
243+
compiler + ' ' + problem_path + '.cpp')
244+
245+
if compile_status == 0:
246+
for i in xrange(num_cases):
247+
status = os.system('timeout 2s ./a.out < ' + os.path.join(
248+
testcases_path, 'Input' + str(i)) + ' > temp_output' + str(i))
249+
if status == 124:
250+
# Time Limit Exceeded
251+
results += [Utilities.colors['BOLD'] + Utilities.colors[
252+
'YELLOW'] + 'TLE' + Utilities.colors['ENDC']]
253+
254+
elif status == 0:
255+
256+
with open('temp_output' + str(i), 'r') as temp_handler, open(os.path.join(testcases_path, 'Output' + str(i)), 'r') as out_handler:
257+
expected_output = out_handler.read().strip()
258+
user_output = temp_handler.read().strip()
259+
260+
expected_outputs += [expected_output]
261+
user_outputs += [user_output]
262+
263+
if expected_output == user_output:
264+
# All Correct
265+
results += [Utilities.colors['BOLD'] + Utilities.colors[
266+
'GREEN'] + 'AC' + Utilities.colors['ENDC']]
267+
else:
268+
# Wrong Answer
269+
results += [Utilities.colors['BOLD'] + Utilities.colors[
270+
'RED'] + 'WA' + Utilities.colors['ENDC']]
271+
272+
else:
273+
# Runtime Error
274+
results += [Utilities.colors['BOLD'] +
275+
Utilities.colors['RED'] + 'RTE' + Utilities.colors['ENDC']]
276+
else:
277+
# Compilation error occurred
278+
message = Utilities.colors['BOLD'] + Utilities.colors[
279+
'RED'] + "Compilation error. Not run against test cases" + Utilities.colors['ENDC'] + "."
280+
print message
281+
sys.exit(0)
282+
283+
else:
284+
print "Unsupported language."
285+
sys.exit(0)
286+
287+
from terminaltables import AsciiTable
288+
table_data = [['Serial No', 'Input',
289+
'Expected Output', 'Your Output', 'Result']]
290+
291+
inputs = Utilities.input_file_to_string(testcases_path, num_cases)
292+
293+
for i in xrange(num_cases):
294+
295+
row = [
296+
i + 1,
297+
inputs[i],
298+
expected_outputs[i],
299+
user_outputs[i] if any(sub in results[i]
300+
for sub in ['AC', 'WA']) else 'N/A',
301+
results[i]
302+
]
303+
304+
table_data.append(row)
305+
306+
table = AsciiTable(table_data)
307+
308+
print table.table
309+
310+
else:
311+
print "Test cases not found locally..."
312+
args = {
313+
'site': testcases_path.split('/')[-3],
314+
'contest': testcases_path.split('/')[-2],
315+
'problem': testcases_path.split('/')[-1],
316+
'force': True
317+
}
318+
Utilities.download_problem_testcases(args)
319+
320+
print "Done. Running your solution against sample cases..."
321+
Utilities.run_solution(problem)
322+
323+
# Clean up temporary files
324+
Utilities.cleanup(num_cases)
325+
146326
@staticmethod
147327
def get_html(url):
148328
"""

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141

4242

4343
data = {"default_site": default_site.strip(), "workdir" : workdir, "cachedir" : cache_dir}
44-
with open("constants.json", "w") as f:
45-
f.write(json.dumps(data))
44+
with open(os.path.join(cache_dir, "constants.json"), "w") as f:
45+
f.write(json.dumps(data, indent=2))
4646

4747
setup(
4848
name = "ACedIt",

0 commit comments

Comments
 (0)