mirror of
https://codeberg.org/andyscott/ziglings.git
synced 2024-11-09 11:40:46 -05:00
Merge pull request #304 from chrboesch/testing_exercise
Added testing exercise.
This commit is contained in:
commit
5cbaa0441f
4 changed files with 129 additions and 5 deletions
|
@ -1219,9 +1219,8 @@ const exercises = [_]Exercise{
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.main_file = "102_testing.zig",
|
.main_file = "102_testing.zig",
|
||||||
.output = "All 1 tests passed.",
|
.output = "",
|
||||||
.run_test = true,
|
.run_test = true,
|
||||||
.skip = true,
|
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.main_file = "999_the_end.zig",
|
.main_file = "999_the_end.zig",
|
||||||
|
|
|
@ -1,10 +1,112 @@
|
||||||
|
//
|
||||||
|
// A big advantage of Zig is the integration of its own test system.
|
||||||
|
// This allows the philosophy of Test Driven Development (TDD) to be
|
||||||
|
// implemented perfectly. Zig even goes one step further than other
|
||||||
|
// languages, the tests can be included directly in the source file.
|
||||||
|
//
|
||||||
|
// This has several advantages. On the one hand it is much clearer to
|
||||||
|
// have everything in one file, both the source code and the associated
|
||||||
|
// test code. On the other hand, it is much easier for third parties
|
||||||
|
// to understand what exactly a function is supposed to do if they can
|
||||||
|
// simply look at the test inside the source and compare both.
|
||||||
|
//
|
||||||
|
// Especially if you want to understand how e.g. the standard library
|
||||||
|
// of Zig works, this approach is very helpful. Furthermore it is very
|
||||||
|
// practical, if you want to report a bug to the Zig community, to
|
||||||
|
// illustrate it with a small example including a test.
|
||||||
|
//
|
||||||
|
// Therefore, in this exercise we will deal with the basics of testing
|
||||||
|
// in Zig. Basically, tests work as follows: you pass certain parameters
|
||||||
|
// to a function, for which you get a return - the result. This is then
|
||||||
|
// compared with the EXPECTED value. If both values match, the test is
|
||||||
|
// passed, otherwise an error message is displayed.
|
||||||
|
//
|
||||||
|
// testing.expect(foo(param1, param2) == expected);
|
||||||
|
//
|
||||||
|
// Also other comparisons are possible, deviations or also errors can
|
||||||
|
// be provoked, which must lead to an appropriate behavior of the
|
||||||
|
// function, so that the test is passed.
|
||||||
|
//
|
||||||
|
// Tests can be run via Zig build system or applied directly to
|
||||||
|
// individual modules using "zig test xyz.zig".
|
||||||
|
//
|
||||||
|
// Both can be used script-driven to execute tests automatically, e.g.
|
||||||
|
// after checking into a Git repository. Something we also make extensive
|
||||||
|
// use of here at Ziglings.
|
||||||
|
//
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
fn add(a: u16, b: u16) u16 {
|
// This is a simple function
|
||||||
|
// that builds a sum from the
|
||||||
|
// passed parameters and returns.
|
||||||
|
fn add(a: f16, b: f16) f16 {
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "simple test" {
|
// The associated test.
|
||||||
|
// It always starts with the keyword "test",
|
||||||
|
// followed by a description of the tasks
|
||||||
|
// of the test. This is followed by the
|
||||||
|
// test cases in curly brackets.
|
||||||
|
test "add" {
|
||||||
|
|
||||||
|
// The first test checks if the sum
|
||||||
|
// of '41' and '1' gives '42', which
|
||||||
|
// is correct.
|
||||||
try testing.expect(add(41, 1) == 42);
|
try testing.expect(add(41, 1) == 42);
|
||||||
|
|
||||||
|
// Another way to perform this test
|
||||||
|
// is as follows:
|
||||||
|
try testing.expectEqual(add(41, 1), 42);
|
||||||
|
|
||||||
|
// This time a test with the addition
|
||||||
|
// of a negative number:
|
||||||
|
try testing.expect(add(5, -4) == 1);
|
||||||
|
|
||||||
|
// And a floating point operation:
|
||||||
|
try testing.expect(add(1.5, 1.5) == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another simple function
|
||||||
|
// that returns the result
|
||||||
|
// of subtracting the two
|
||||||
|
// parameters.
|
||||||
|
fn sub(a: f16, b: f16) f16 {
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The corresponding test
|
||||||
|
// is not much different
|
||||||
|
// from the previous one.
|
||||||
|
// Except that it contains
|
||||||
|
// an error that you need
|
||||||
|
// to correct.
|
||||||
|
test "sub" {
|
||||||
|
try testing.expect(sub(10, 5) == 6);
|
||||||
|
|
||||||
|
try testing.expect(sub(3, 1.5) == 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function divides the
|
||||||
|
// numerator by the denominator.
|
||||||
|
// Here it is important that the
|
||||||
|
// denominator must not be zero.
|
||||||
|
// This is checked and if it
|
||||||
|
// occurs an error is returned.
|
||||||
|
fn divide(a: f16, b: f16) !f16 {
|
||||||
|
if (b == 0) return error.DivisionByZero;
|
||||||
|
return a / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "divide" {
|
||||||
|
try testing.expect(divide(2, 2) catch unreachable == 1);
|
||||||
|
try testing.expect(divide(-1, -1) catch unreachable == 1);
|
||||||
|
try testing.expect(divide(10, 2) catch unreachable == 5);
|
||||||
|
try testing.expect(divide(1, 3) catch unreachable == 0.3333333333333333);
|
||||||
|
|
||||||
|
// Now we test if the function returns an error
|
||||||
|
// if we pass a zero as denominator.
|
||||||
|
// But which error needs to be tested?
|
||||||
|
try testing.expectError(error.???, divide(15, 0));
|
||||||
}
|
}
|
||||||
|
|
8
patches/patches/102_testing.patch
Normal file
8
patches/patches/102_testing.patch
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
86c86
|
||||||
|
< try testing.expect(sub(10, 5) == 6);
|
||||||
|
---
|
||||||
|
> try testing.expect(sub(10, 5) == 5);
|
||||||
|
111c111
|
||||||
|
< try testing.expectError(error.???, divide(15, 0));
|
||||||
|
---
|
||||||
|
> try testing.expectError(error.DivisionByZero, divide(15, 0));
|
|
@ -93,7 +93,7 @@ pub fn addCliTests(b: *std.Build, exercises: []const Exercise) *Step {
|
||||||
const case_step = createCase(b, "case-3");
|
const case_step = createCase(b, "case-3");
|
||||||
|
|
||||||
for (exercises[0 .. exercises.len - 1]) |ex| {
|
for (exercises[0 .. exercises.len - 1]) |ex| {
|
||||||
if (ex.skip) continue;
|
if (ex.skip or ex.run_test) continue;
|
||||||
|
|
||||||
if (ex.hint) |hint| {
|
if (ex.hint) |hint| {
|
||||||
const n = ex.number();
|
const n = ex.number();
|
||||||
|
@ -249,6 +249,21 @@ fn check_output(step: *Step, exercise: Exercise, reader: Reader) !void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (exercise.run_test) {
|
||||||
|
{
|
||||||
|
const actual = try readLine(reader, &buf) orelse "EOF";
|
||||||
|
const expect = b.fmt("Testing {s}...", .{exercise.main_file});
|
||||||
|
try check(step, exercise, expect, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const actual = try readLine(reader, &buf) orelse "EOF";
|
||||||
|
try check(step, exercise, "", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const actual = try readLine(reader, &buf) orelse "EOF";
|
const actual = try readLine(reader, &buf) orelse "EOF";
|
||||||
const expect = b.fmt("Compiling {s}...", .{exercise.main_file});
|
const expect = b.fmt("Compiling {s}...", .{exercise.main_file});
|
||||||
|
|
Loading…
Reference in a new issue