Created
January 24, 2013 03:50
-
-
Save johnliu/4617491 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import inspect | |
import os | |
import subprocess as sp | |
import sys | |
""" | |
A simple testing framework for this shell. | |
To run, call `python test.py` or `./test.py` after changing the permissions of this file to | |
executable. | |
""" | |
def expect(value_a, value_b, message): | |
""" | |
Checks if value_a is equal to value_b, if not, display the message. | |
""" | |
if value_a != value_b: | |
sys.stdout.write('\033[31mFAILURE\033[0m ') | |
sys.stdout.write(inspect.stack()[1][3] + ': ') | |
if message: | |
print '%s, expected "%s" to be "%s"' % (message, value_a, value_b) | |
else: | |
print 'expected "%s" to be "%s"' % (value_a, value_b) | |
else: | |
sys.stdout.write('\033[32mSUCCESS\033[0m ') | |
sys.stdout.write(inspect.stack()[1][3] + ': ') | |
if message: | |
print '%s' % message | |
class Test: | |
""" | |
The Test class, containing all tests for the program. | |
""" | |
SHELL_PROMPT = 'ece353sh$ ' | |
def setup_once(self): | |
# Build the program first, and suppress output. | |
sp.call(['make', '-C', '..'], stdout=sp.PIPE) | |
def tear_down_once(self): | |
# Remove program files. | |
os.remove('../main.o') | |
os.remove('../ece353sh') | |
def setup(self): | |
# Start the process. | |
self.process = sp.Popen(['../ece353sh'], | |
stdout=sp.PIPE, | |
stdin=sp.PIPE, | |
stderr=sp.STDOUT, | |
shell=True) | |
def tear_down(self): | |
# Kill the process we're testing. | |
if self.process.poll() is None: | |
self.process.kill() | |
def test_exit(self): | |
# Get the output and exit code. | |
output = self.process.communicate(input='exit\n')[0] | |
exit_code = self.process.poll() | |
# The output should be just one shell prompt. | |
expect(output, self.SHELL_PROMPT, 'testing stdout stream') | |
# The shell should have properly exited (with status 0). | |
expect(exit_code, 0, 'testing exit code') | |
def test_exit_with_whitespace(self): | |
# Get the output and exit code. | |
output = self.process.communicate(input='\t\t\r \nexit \f\v\r\n')[0] | |
exit_code = self.process.poll() | |
# The output should be two shell prompts, since a \n (newline) was fed into the input. | |
expect(output, self.SHELL_PROMPT + self.SHELL_PROMPT, 'testing stdout stream') | |
# The shell should have properly exited (with status 0). | |
expect(exit_code, 0, 'testing exit code') | |
def test_invalid_input(self): | |
output = self.process.communicate(input='this_is_completely_invalid\nexit\n')[0] | |
# There should be two shell prompts, since we exit after typing in the invalid command. | |
expected = "%serror: command not found: %s\n%s" % (self.SHELL_PROMPT, "this_is_completely_invalid", self.SHELL_PROMPT) | |
expect(output, expected, 'testing invalid input') | |
def test_valid_ls(self): | |
output = self.process.communicate(input='/bin/ls -a .\nexit\n')[0] | |
# First retrieve the known output from using ls on 'sh'. | |
ls_output = sp.Popen(['ls', '-a', '.'], stdout=sp.PIPE).communicate()[0] | |
# The expected output is a shell prompt (the initial one), followed by the ls output and | |
# finally the next shell prompt before exiting. | |
expect(output, | |
'%s%s%s' % (self.SHELL_PROMPT, ls_output, self.SHELL_PROMPT), | |
'testing ls command') | |
def test_long_input(self): | |
input_str = ' ' * 600 + 'exit\n' | |
output = self.process.communicate(input=input_str)[0] | |
exit_code = self.process.poll() | |
# Expect an input with only one prompt. | |
expect(output, self.SHELL_PROMPT, 'testing long input with spaces') | |
# Expect the exit code to be 0. | |
expect(exit_code, 0, 'testing exit code') | |
def test_long_continuous_input(self): | |
# Create temporary files | |
long_file_name = '1' * 200 | |
os.system('mkdir -p %(n)s/%(n)s/%(n)s/%(n)s' % {'n': long_file_name}) | |
os.system('touch %(n)s/%(n)s/%(n)s/%(n)s/hi' % {'n': long_file_name}) | |
# First retrieve the known output from using ls on 'sh'. | |
ls_output = sp.Popen(['ls', '%(n)s/%(n)s/%(n)s/%(n)s' % {'n': long_file_name}], | |
stdout=sp.PIPE).communicate()[0] | |
output = self.process.communicate( | |
input='/bin/ls %(n)s/%(n)s/%(n)s/%(n)s\nexit\n' % {'n': long_file_name})[0] | |
expect(output, '%s%s%s' % (self.SHELL_PROMPT, ls_output, self.SHELL_PROMPT), | |
'testing long input') | |
# Clean up temporary filess | |
os.system('rm -rf %s' % long_file_name) | |
def test_long_discontinuous_input(self): | |
# Create temporary files | |
long_file_names = ['1' * 200, ' ' * 400, '3' * 200] | |
os.system('touch %s' % long_file_names[0]) | |
os.system('touch %s' % long_file_names[2]) | |
os.system('echo "asdf" > %s' % (long_file_names[2])) | |
# First retrieve the known output from using ls on 'sh'. | |
cat_output = sp.Popen(['cat', long_file_names[0], long_file_names[2]], | |
stdout=sp.PIPE).communicate()[0] | |
output = self.process.communicate( | |
input='/bin/cat %s %s %s\nexit\n' % tuple(long_file_names))[0] | |
expect(output, '%s%s%s' % (self.SHELL_PROMPT, cat_output, self.SHELL_PROMPT), | |
'testing long input') | |
# Clean up temporary filess | |
os.system('rm %s' % long_file_names[0]) | |
os.system('rm %s' % long_file_names[2]) | |
def test_max_args(self): | |
pass | |
def run(obj, *args, **kwargs): | |
""" | |
Method to run all methods on a given object. | |
""" | |
# List of files to ignore. | |
ignore_list = ['setup', 'setup_once', 'tear_down', 'tear_down_once'] | |
# Get all the setup functions. | |
setup = getattr(obj, 'setup') | |
tear_down = getattr(obj, 'tear_down') | |
setup_once = getattr(obj, 'setup_once') | |
tear_down_once = getattr(obj, 'tear_down_once') | |
# Setup for testing. | |
setup_once() | |
for name in dir(obj): | |
if name not in ignore_list: | |
attribute = getattr(obj, name) | |
# Run each method in the test class. | |
if inspect.ismethod(attribute): | |
setup() | |
attribute(*args, **kwargs) | |
tear_down() | |
# Tear down after testing. | |
tear_down_once() | |
if __name__ == '__main__': | |
run(Test()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment