README.md
1# libgrading
2
3This is a simple library for grading C- and C++-language assignments.
4It runs each test case in a child process in order to capture common
5programming errors such as infinite loops and segmentation faults.
6
7## Get it
8
9libgrading is available as a binary package for FreeBSD, a Homebrew tap for
10macOS and a PPA for Ubuntu Linux.
11You can also build from source.
12
13### FreeBSD
14
15You can install a binary package with `pkg install libgrading`
16or compile the `devel/libgrading` port
17[from source](https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/ports-using.html).
18
19### macOS
20
21First, install [Homebrew](https://brew.sh).
22Then, run
23`brew install trombonehero/homebrew-grading/libgrading`.
24
25### Ubuntu 18.04
26
27You can install a binary package from my
28[grading software PPA](https://launchpad.net/~professor-jon/+archive/ubuntu/grading-software):
29
30```terminal
31sudo add-apt-repository ppa:professor-jon/grading-software
32sudo apt-get update
33sudo apt install libgrading-dev
34```
35
36
37## Build it
38
39The libgrading source code is
40[hosted on GitHub](https://github.com/trombonehero/libgrading).
41Releases are available on
42[the GitHub releases page](https://github.com/trombonehero/libgrading/releases),
43and you can always get the most latest version by running either:
44
45~~~sh
46$ git clone https://github.com/trombonehero/libgrading.git
47$ svn checkout https://github.com/trombonehero/libgrading
48~~~
49
50First, install [libdistance](http://monkey.org/~jose/software/libdistance/).
51Then:
52
53~~~sh
54$ mkdir build
55$ cd build
56$ cmake .. # or cmake -G Ninja ..
57$ make # or ninja
58~~~
59
60## Use it
61
62~~~cpp
63#include <libgrading.h>
64using namespace grading;
65using namespace std;
66
67
68//
69// One way to define tests is to define a domain-relevant expectation
70// (inputs and outputs) and a function that will check that expectation:
71//
72struct AdditionExpectation
73{
74 int x;
75 int y;
76 int sum;
77};
78
79void TestStudentFn(const AdditionExpectation& expected)
80{
81 // In a real test suite, you'd link against submitted code.
82 // For this demo, we'll use a lambda.
83 auto studentFunction = [](int x, int y) { return x + y + 1; };
84
85 int sum = studentFunction(expected.x, expected.y);
86 CheckInt(expected.sum, sum)
87 << "some more detail to be output if this check fails";
88}
89
90
91//
92// You can define a grading::TestSuite declaratively, if you like such things
93// (vs. using the TestBuilder class, as inside the `main` function below):
94//
95const TestSuite testClosures =
96{
97 // This is an example of a Test derived from an expectation and an
98 // evaluation function.
99 {
100 "simple addition test",
101 " - first part of the long description\n"
102 " - second part of the long description",
103 TestStudentFn,
104 { 2, 2, 5 },
105 0, // timeout: optional, 0 (the default) means forever
106 1, // weight to give this test (optional, default 1)
107 },
108
109 // Tests can also be created from self-contained test closures:
110 {
111 "test name",
112 "long description ...",
113 []()
114 {
115 int x = foo();
116 CheckInt(42, x)
117 << "the result of calling foo() should be 42"
118 ;
119
120 // segmentation faults will be caught and handled
121 // properly when running with --strategy=separated
122 // or --strategy=sandboxed (the default)
123 double *x = nullptr;
124 double y = *x;
125 },
126 },
127
128 // ...
129};
130
131
132//
133// You need to write a `main` function to actually run the tests,
134// but it can be very simple indeed:
135//
136int main(int argc, char *argv[])
137{
138 const TestSuite::Statistics stats = tests.Run(argc, argv);
139
140 // optionally do something with the stats, such as:
141 cout << "Grade: " << stats.score << endl;
142
143 return 0;
144}
145~~~
146