--- /dev/null
+{
+ "version": "0.1",
+ "introduction": "You have one hour to complete the following tasks involving Git, POSIX, and Python. If at any point you would like clarification, please do not hesitate to ask. If at any point you are unable to complete a task, you may also ask for help so that you can proceed to the next task, but doing so will be considered equivalent to not completing that task. You may search the web, man pages, etc..",
+ "questions": [
+ {
+ "class": "ScriptQuestion",
+ "intepreter": "sh",
+ "id": "find",
+ "prompt": "Use a single shell command to create a list of all files with names ending in .dat in or below this directory in a file called all-dat-files.txt.",
+ "answer": "find . -name '*.dat' -print > all-dat-files.txt",
+ "setup": [
+ "mkdir -p inputs/{a,b,c}",
+ "touch inputs/{,a/,b/,c/}{d,e,f}.dat"
+ ],
+ "teardown": [
+ "ls all-dat-files.txt",
+ "sort all-dat-files.txt"
+ ],
+ "help": "http://pubs.opengroup.org/onlinepubs/009696699/utilities/find.html",
+ "tags": [
+ "POSIX"
+ ]
+ },
+ {
+ "class": "ScriptQuestion",
+ "intepreter": "sh",
+ "id": "make",
+ "prompt": [
+ "The analyze.py program takes exactly two arguments: the name of its input file and the name of its output file, in that order. For example, if inputs/a.dat changes, running GNU make will execute the command:",
+ "",
+ " ./analyze.py inputs/a.dat outputs/a.out",
+ "",
+ "Edit the file Makefile in the current directory so that if any .dat file in the inputs directory changes, the program analyze.py is run to create a file named .out in the outputs directory."
+ ],
+ "multiline": true,
+ "timeout": null,
+ "answer": [
+ "cat >> Makefile <<\\EOF",
+ "outputs/%.out : inputs/%.dat",
+ "\t./analyze.py \"$<\" \"$@\"",
+ "EOF"
+ ],
+ "setup": [
+ "mkdir -p inputs/{a,b,c}",
+ "for x in inputs/{,a/,b/,c/}{d,e,f}.dat; do echo \"$x\" > \"$x\"; done",
+ "cat > analyze.py <<EOF",
+ "#!/usr/bin/env python",
+ "import os",
+ "import sys",
+ "input,output = sys.argv[1:3]",
+ "dirname = os.path.dirname(output)",
+ "if dirname and not os.path.isdir(dirname):",
+ " os.makedirs(dirname)",
+ "text = open(input, 'r').read()",
+ "with open(output, 'w') as f:",
+ " f.write('==START==\\n')",
+ " f.write(text)",
+ " f.write('==END==\\n')",
+ "EOF",
+ "chmod 755 analyze.py",
+ "cat > Makefile <<\\EOF",
+ "OUTPUTS = $(sort $(patsubst inputs/%.dat,outputs/%.out,$(wildcard inputs/*.dat) $(wildcard inputs/*/*.dat)))",
+ "",
+ "all: $(OUTPUTS)",
+ "",
+ "# Add your rule here, using a command like:",
+ "#",
+ "# ./analyze.py inputs/a.dat outputs/a.out",
+ "#",
+ "# to generate the output files. You should only need one rule.",
+ "EOF"
+ ],
+ "teardown": [
+ "make > make.log",
+ "OUTPUTS=$(find outputs | sort)",
+ "if [ -n \"$OUTPUTS\" ]; then head $OUTPUTS; fi"
+ ],
+ "help": [
+ "http://pubs.opengroup.org/onlinepubs/009696699/utilities/make.html",
+ "http://www.gnu.org/software/make/manual/html_node/Pattern-Rules.html"
+ ],
+ "tags": [
+ "make"
+ ]
+ },
+ {
+ "class": "ScriptQuestion",
+ "intepreter": "sh",
+ "id": "git commit",
+ "prompt": [
+ "Commit your changes to Makefile to the version control repository.",
+ "The commit message should be `Building .out files for .dat files`."
+ ],
+ "multiline": true,
+ "timeout": null,
+ "answer": [
+ "git commit -am 'Building .out files for .dat files'"
+ ],
+ "environment": {
+ "GIT_AUTHOR_NAME": "A U Thor",
+ "GIT_AUTHOR_EMAIL": "author@example.com",
+ "GIT_COMMITTER_NAME": "C O Mitter",
+ "GIT_COMMITTER_EMAIL": "committer@example.com",
+ "GIT_AUTHOR_DATE": "1970-01-01T00:01:00Z",
+ "GIT_COMMITTER_DATE": "1970-01-01T00:01:00Z"
+ },
+ "setup": [
+ "export GIT_AUTHOR_NAME='A U Thor'",
+ "export GIT_AUTHOR_EMAIL=author@example.com",
+ "export GIT_COMMITTER_NAME='C O Mitter'",
+ "export GIT_COMMITTER_EMAIL=committer@example.com",
+ "export GIT_AUTHOR_DATE=1970-01-01T00:00:00Z",
+ "export GIT_COMMITTER_DATE=\"$GIT_AUTHOR_DATE\"",
+ "git init",
+ "echo '# Original stuff' > Makefile",
+ "git add Makefile",
+ "git commit -m 'Add a Makefile'",
+ "echo '# New stuff' >> Makefile"
+ ],
+ "teardown": [
+ "git log -p"
+ ],
+ "help": "http://www.kernel.org/pub/software/scm/git/docs/git-commit.html",
+ "tags": [
+ "version-control"
+ ]
+ },
+ {
+ "class": "ScriptQuestion",
+ "interpreter": "sh",
+ "id": "git merge",
+ "prompt": [
+ "Merge your Makefile changes with some upstream work.",
+ "Use Git's auto-generated commit message, which should be:",
+ "",
+ " Merge branch 'origin/master'",
+ "",
+ " Conflicts:",
+ " \tMakefile",
+ "",
+ "You may want to drop into a POSIX shell (e.g. via `!bash`) to do this."
+ ],
+ "multiline": true,
+ "timeout": null,
+ "allow_interactive": true,
+ "answer": [
+ "git merge origin/master",
+ "sed -i -e '/<<<.*\\|>>>.*/d' -e 's/===.*//' Makefile",
+ "git add Makefile",
+ "git commit --file .git/MERGE_MSG"
+ ],
+ "environment": {
+ "GIT_AUTHOR_NAME": "A U Thor",
+ "GIT_AUTHOR_EMAIL": "author@example.com",
+ "GIT_COMMITTER_NAME": "C O Mitter",
+ "GIT_COMMITTER_EMAIL": "committer@example.com",
+ "GIT_AUTHOR_DATE": "1970-01-01T00:03:00Z",
+ "GIT_COMMITTER_DATE": "1970-01-01T00:03:00Z"
+ },
+ "setup": [
+ "export GIT_AUTHOR_NAME='A U Thor'",
+ "export GIT_AUTHOR_EMAIL=author@example.com",
+ "export GIT_COMMITTER_NAME='C O Mitter'",
+ "export GIT_COMMITTER_EMAIL=committer@example.com",
+ "export GIT_AUTHOR_DATE=1970-01-01T00:00:00Z",
+ "export GIT_COMMITTER_DATE=\"$GIT_AUTHOR_DATE\"",
+ "git init",
+ "cat > Makefile <<\\EOF",
+ "OUTPUTS = $(sort $(patsubst inputs/%.dat,outputs/%.out,$(wildcard inputs/*.dat) $(wildcard inputs/*/*.dat)))",
+ "",
+ "all: $(OUTPUTS)",
+ "",
+ "# Add your rule here, using a command like:",
+ "#",
+ "# ./analyze.py inputs/a.dat outputs/a.out",
+ "#",
+ "# to generate the output files. You should only need one rule.",
+ "EOF",
+ "git add Makefile",
+ "git commit -m 'Initial Makefile with OUTPUTS'",
+ "export GIT_AUTHOR_DATE=1970-01-01T00:01:00Z",
+ "export GIT_COMMITTER_DATE=\"$GIT_AUTHOR_DATE\"",
+ "cat >> Makefile <<\\EOF",
+ "outputs/%.out : inputs/%.dat",
+ "\t./analyze.py \"$<\" \"$@\"",
+ "EOF",
+ "git commit -am 'Building .out files for .dat files'",
+ "export GIT_AUTHOR_DATE=1970-01-01T00:02:00Z",
+ "export GIT_COMMITTER_DATE=\"$GIT_AUTHOR_DATE\"",
+ "git checkout -b origin/master HEAD^",
+ "cat >> Makefile <<\\EOF",
+ "clean:",
+ "\trm -rf outputs/",
+ "EOF",
+ "git commit -am \"Makefile: Add a 'clean' target\"",
+ "git checkout master"
+ ],
+ "teardown": [
+ "cat Makefile",
+ "git log --all --oneline --graph --decorate"
+ ],
+ "help": "http://www.kernel.org/pub/software/scm/git/docs/git-merge.html",
+ "tags": [
+ "branch",
+ "merge"
+ ]
+ },
+ {
+ "class": "ScriptQuestion",
+ "intepreter": "sh",
+ "id": "test running total",
+ "prompt": [
+ "The analyze.py program contains a function called running_total, which is supposed to calculate the total of each strictly increasing sequence of numbers in a list:",
+ "",
+ " running_total([1, 2, 1, 8, 9, 2]) == [3, 18, 2]",
+ " running_total([1, 3, 4, 2, 5, 4, 6, 9]) == [8, 7, 19]",
+ "",
+ "In the file test_analyze.py, write the four (4) unit tests that you think are most important to run to test this function. Do not test for cases of invalid input (i.e., inputs that are strings, lists of lists, or anything else that isn't a flat list of numbers).",
+ "",
+ "You may want to drop into a POSIX shell (e.g. via `!bash`) to do this.",
+ "You should be able to run your tests using `nosetests`."
+ ],
+ "multiline": true,
+ "timeout": null,
+ "allow_interactive": true,
+ "answer": [
+ "cat >> test_analyze.py <<EOF",
+ "from analyze import running_total",
+ "",
+ "def test_empty():",
+ " assert running_total([]) == []",
+ "",
+ "def test_equal():",
+ " assert running_total([1, 1]) == [1, 1]",
+ "",
+ "def test_negative():",
+ " assert running_total([1, 5, -5, -3]) == [6, -8]",
+ "",
+ "def test_float():",
+ " assert running_total([1.0, 5.0, 2.0]) == [6.0, 2.0]",
+ "EOF"
+ ],
+ "setup": [
+ "cat >> analyze.py <<EOF",
+ "def running_total(iterator):",
+ " \"calculate the total of each increasing sequence of numbers\"",
+ " totals = []",
+ " last = None",
+ " for i in iterator:",
+ " if last is not None and i > last:",
+ " totals[-1] += i",
+ " else:",
+ " totals.append(i)",
+ " last = i",
+ " return totals",
+ "EOF"
+ ],
+ "teardown": [
+ "cat > run-nose-without-time.py <<EOF",
+ "import nose",
+ "import nose.result",
+ "def printSummary(self, start, stop):",
+ " self.stream.write('8<-----\\n') # avoid errors/failures",
+ " self.stream.write('Ran {} test(s)\\n'.format(self.testsRun))",
+ " self.stream.write('{} failures\\n'.format(len(self.failures)))",
+ " self.stream.write('{} errors\\n'.format(len(self.errors)))",
+ "nose.result.TextTestResult.printSummary = printSummary",
+ "print(nose.run())",
+ "EOF",
+ "python run-nose-without-time.py 2>&1 | grep -A5 '8<-----'",
+ "cat >> analyze.py <<EOF",
+ "def running_total(iterator):",
+ " \"broken implemenation\"",
+ " return 'muahaha'",
+ "EOF",
+ "python run-nose-without-time.py 2>&1 | grep -A5 '8<-----'"
+ ],
+ "help": [
+ "https://nose.readthedocs.org/en/latest/writing_tests.html#writing-tests",
+ "https://nose.readthedocs.org/en/latest/writing_tests.html#test-functions"
+ ],
+ "tags": [
+ "python",
+ "testing"
+ ]
+ },
+ {
+ "class": "ScriptQuestion",
+ "intepreter": "sh",
+ "id": "shell scripting",
+ "prompt": [
+ "Write a shell script called do-many.sh that runs power2.py for many different numbers. For example:",
+ "",
+ " ./do-many.sh 27 9 35",
+ "",
+ "must produce:",
+ "",
+ " 16 8 2 1",
+ " 8 1",
+ " 32 2 1",
+ "",
+ "as its output. You do not need to do error-checking on the command-line parameters, i.e., you may assume that they are all non-negative integers."
+ ],
+ "multiline": true,
+ "timeout": null,
+ "answer": [
+ "cat >> do-many.sh <<\\EOF",
+ "#!/bin/sh",
+ "for number in \"$@\"; do",
+ " ./power2.py \"$number\"",
+ "done",
+ "EOF",
+ "chmod 755 do-many.sh"
+ ],
+ "setup": [
+ "cat >> power2.py <<EOF",
+ "#!/usr/bin/env python",
+ "import sys",
+ "print('This is a dummy implementation, called with:')",
+ "print(sys.argv)",
+ "EOF",
+ "chmod 755 power2.py"
+ ],
+ "teardown": [
+ "for x in '27 9' '35 14 1'; do",
+ "\techo ./do-many.sh $x",
+ "\t./do-many.sh $x",
+ "done"
+ ],
+ "help": "http://pubs.opengroup.org/onlinepubs/009696699/utilities/xcu_chap02.html",
+ "tags": [
+ "POSIX"
+ ]
+ }
+ ]
+}