Skip to content

Commit 143940e

Browse files
authored
Merge pull request #46 from datacamp/feat/ide_exercise
[CX-844] Add basic run functionality
2 parents 36f24bf + 14e23d6 commit 143940e

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed

shellwhat/checks/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
has_expr_exit_code,
77
)
88

9+
from shellwhat.run_file import run
10+
911
from protowhat.checks.check_logic import fail, multi, check_not, check_or, check_correct
1012
from protowhat.checks.check_simple import has_chosen, success_msg
1113
from protowhat.checks.check_files import check_file, has_dir

shellwhat/run_file.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import subprocess
2+
3+
from shellwhat.reporter import Reporter
4+
5+
6+
def run(state):
7+
"""Run the student file focused by ``check_file``.
8+
9+
Args:
10+
state (State): state as passed by the SCT chain. Don't specify this explicitly.
11+
12+
:Example:
13+
14+
Suppose the student has a file ``script.sh`` in ``/home/repl/``::
15+
16+
echo 'test'
17+
18+
We can check if the file runs with this SCT::
19+
20+
Ex().check_file(
21+
"script.sh"
22+
).run()
23+
"""
24+
student_result, error = run_file(state.path)
25+
return state.to_child(
26+
student_result=student_result,
27+
reporter=Reporter(state.reporter, errors=[error] if error else []),
28+
)
29+
30+
31+
def run_file(path):
32+
output, exception = None, None
33+
try:
34+
output = subprocess.check_output(path.__str__(), stderr=subprocess.PIPE)
35+
except subprocess.CalledProcessError as e:
36+
exception = (
37+
"returned non-zero exit status "
38+
+ str(e.returncode)
39+
+ " "
40+
+ str(e.stderr, "utf-8")
41+
)
42+
except PermissionError:
43+
exception = "{} is not executable".format(path)
44+
45+
try:
46+
output = str(output, "utf-8")
47+
except TypeError:
48+
pass
49+
50+
return output, exception

tests/test_run.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import pytest
2+
import os
3+
import subprocess
4+
from pexpect import replwrap
5+
from tempfile import TemporaryDirectory
6+
from pathlib import Path
7+
8+
from shellwhat.reporter import Reporter
9+
from shellwhat.State import State
10+
from shellwhat.run_file import run
11+
12+
13+
@pytest.fixture
14+
def state():
15+
state = State(
16+
student_code="some code\x1b[39;49m",
17+
solution_code="some code",
18+
pre_exercise_code="",
19+
student_conn=replwrap.bash(),
20+
solution_conn=None,
21+
student_result="stdout stuff",
22+
solution_result=None,
23+
reporter=Reporter(),
24+
)
25+
state.root_state = state
26+
return state
27+
28+
29+
@pytest.fixture(scope="function")
30+
def tempdir():
31+
cwd = os.getcwd()
32+
with TemporaryDirectory() as tmp:
33+
os.chdir(tmp)
34+
yield tmp
35+
os.chdir(cwd)
36+
37+
38+
def test_run_student_code_is_run(state, tempdir):
39+
# Given
40+
state.student_code = "touch testfile.sh\n"
41+
state.solution_code = ""
42+
43+
filedir = tempdir + "/myscript.sh"
44+
with open(filedir, "w+") as f:
45+
f.write("#!/bin/bash\n")
46+
f.write(state.student_code)
47+
48+
subprocess.run(["chmod", "+x", filedir])
49+
state.path = Path(filedir)
50+
51+
# When
52+
run(state)
53+
54+
# Then
55+
assert os.path.exists(filedir)
56+
57+
58+
def test_run_student_code_output(state, tempdir):
59+
# Given
60+
state.student_code = "echo 'test'\n"
61+
state.solution_code = ""
62+
63+
filedir = tempdir + "/myscript.sh"
64+
with open(filedir, "w+") as f:
65+
f.write("#!/bin/bash\n")
66+
f.write(state.student_code)
67+
68+
subprocess.run(["chmod", "+x", filedir])
69+
state.path = Path(filedir)
70+
71+
# When
72+
state = run(state)
73+
74+
# Then
75+
assert state.student_result == "test\n"
76+
77+
78+
def test_run_student_code_env_var_access(state, tempdir):
79+
# Given
80+
os.environ["ENVVARTEST"] = "envtest"
81+
state.student_code = "echo $ENVVARTEST\n"
82+
state.solution_code = ""
83+
84+
filedir = tempdir + "/myscript.sh"
85+
with open(filedir, "w+") as f:
86+
f.write("#!/bin/bash\n")
87+
f.write(state.student_code)
88+
89+
subprocess.run(["chmod", "+x", filedir])
90+
state.path = Path(filedir)
91+
92+
# When
93+
state = run(state)
94+
95+
# Then
96+
assert state.student_result == "envtest\n"
97+
98+
99+
def test_run_student_code_error(state, tempdir):
100+
# Given
101+
state.student_code = '>&2 echo "Noooo!"\nexit 5\n'
102+
state.solution_code = ""
103+
104+
filedir = tempdir + "/myscript.sh"
105+
with open(filedir, "w+") as f:
106+
f.write("#!/bin/bash\n")
107+
f.write(state.student_code)
108+
109+
subprocess.run(["chmod", "+x", filedir])
110+
state.path = Path(filedir)
111+
112+
# When
113+
state = run(state)
114+
115+
# Then
116+
assert state.reporter.errors == ["returned non-zero exit status 5 Noooo!\n"]
117+
118+
119+
def test_run_student_not_executable(state, tempdir):
120+
# Given
121+
state.student_code = "echo 'test'\n"
122+
state.solution_code = ""
123+
124+
filedir = tempdir + "/myscript.sh"
125+
with open(filedir, "w+") as f:
126+
f.write("#!/bin/bash\n")
127+
f.write(state.student_code)
128+
129+
subprocess.run(["chmod", "-x", filedir])
130+
state.path = Path(filedir)
131+
132+
# When
133+
state = run(state)
134+
135+
# Then
136+
assert state.reporter.errors == [filedir + " is not executable"]

0 commit comments

Comments
 (0)