Skip to content

Commit

Permalink
Merge pull request #56 from pedropark99/revision-7
Browse files Browse the repository at this point in the history
Add a revision for Chapter 8
  • Loading branch information
pedropark99 authored Oct 4, 2024
2 parents 42141b2 + baf6df1 commit 28c7d10
Show file tree
Hide file tree
Showing 19 changed files with 98 additions and 109 deletions.
61 changes: 29 additions & 32 deletions Chapters/03-unittests.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ knitr::opts_chunk$set(

# Unit tests {#sec-unittests}

In this chapter, I want to dive in on how unit tests are developed in
Zig. We are going to talk about what is the testing wokflow in Zig, and
In this chapter, I want to dive in on how unit tests are done in
Zig. We are going to talk about what is the testing workflow in Zig, and
also, about the `test` command from the `zig` compiler.


Expand All @@ -35,9 +35,10 @@ is a function that receives a logical test as input. If this logical test
results in `true`, then, the test passes. But if it results
in `false`, then, the test fails.

You can write any Zig code you want inside of each `test` block.
You can write any Zig code you want inside a `test` block.
Part of this code might be some necessary commands to setup your testing
environment, or just initializing some necessary objects.
environment, or just initializing some objects that you need to use
in your unit tests.

```{zig}
#| build_type: "test"
Expand All @@ -54,15 +55,15 @@ You can have multiple `test` blocks written on the same Zig module.
Also, you can mix `test` blocks with your source code, with no problems
or consequences. If you mix `test` blocks with your normal source code,
when you execute the `build`, `build-exe`, `build-obj` or `build-lib` commands from the
`zig` compiler that we exposed at @sec-compile-code, these `test` blocks are automatically ignored by the
compiler.
`zig` compiler that we exposed at @sec-compile-code, these `test` blocks are automatically
ignored by the compiler.

In other words, the `zig` compiler only builds and execute your tests
In other words, the `zig` compiler builds and execute your unit tests only
when you ask it to. By default, the compiler always ignore `test`
blocks written in your Zig modules. The compiler normally checks only if
there are any syntax errors in these `test` blocks.

If you look at the source code for most of the files present in the
If you take a look at the source code for most of the files present in the
Zig Standard Library[^zig-std-lib], you can see that the `test` blocks
are written together with the normal source code of the library.
You can see this for example, at the [`array_list` module](https://github.com/ziglang/zig/blob/master/lib/std/array_list.zig)[^zig-array].
Expand All @@ -74,8 +75,8 @@ Each programmer might have a different opinion on this.
Some of them might prefer to keep unit tests separate from the actual
source code of their application. If that is your case, you can
simply create a separate `tests` folder in your project, and
start writing Zig modules that contains only unit tests (as would normally do on a Python project with `pytest`, for example),
and everything will work fine.
start writing Zig modules that contains only unit tests (as you would normally do
on a Python project with `pytest`, for example), and everything will work fine.
It boils down to which is your preference here.

[^zig-std-lib]: <https://github.com/ziglang/zig/tree/master/lib/std>
Expand All @@ -86,9 +87,9 @@ It boils down to which is your preference here.

If the `zig` compiler ignores any `test` block by default, how can
you compile and run your unit tests? The answer is the `test` command from
the `zig` compiler. By running `zig test` command, the compiler will
find every instance of `test` block in your Zig module, and, it will
compile and run the unit tests you wrote.
the `zig` compiler. By running the `zig test` command, the compiler will
find every instance of a `test` block in your Zig modules, and, it will
compile and run the unit tests that you wrote.


```bash
Expand All @@ -104,15 +105,14 @@ All 1 tests passed.
## Testing memory allocations

One of the advantages of Zig is that it offers great tools
that hep us, programmers, to avoid (but also detect) memory problems, such as
that help us, programmers, to avoid (but also detect) memory problems, such as
memory leaks and double-frees. The `defer` keyword
is especially helpful in this regard.

When developing your
source code, you, the programmer, is responsible for making
When developing your source code, you, the programmer, is responsible for making
sure that your code do not produce such problems. However,
you can also use a special type of allocator object in Zig,
that is capable of automatically detect such problems for you.
you can also use a special type of an allocator object in Zig
that is capable of automatically detecting such problems for you.
This is the `std.testing.allocator` object.
This allocator object offers some basic memory safety detection
features, which are capable of detecting memory leaks.
Expand All @@ -129,18 +129,15 @@ write unit tests for these functions, where you provide the
`std.testing.allocator` object as input to these functions.

Look at the example below, where I'm defining a function that clearly causes
a memory leak. Because we allocate memory with the allocator object,
but we do not free this allocated memory in any point. So, when the function
a memory leak. Because we allocate memory, but, at the same time,
we do not free this allocated memory at any point. So, when the function
returns, we lose the reference to the `buffer` object, which contains
the allocated memory, and, as a result, we can no longer free this memory.

Notice that, inside a `test` block I execute this function with
the `std.testing.allocator`. Since no visible errors were raised inside
the `test` block, the `zig` compiler completes the process indicating that
the unit tests performed inside the `test` block labeled as `"memory leak`
have all passed. But despite this result, the allocator object was capable
the `std.testing.allocator`. The allocator object was capable
of looking deeper in our program, and detecting the memory leak. As a result,
this allocator object returns a message "tests leaked memory", and also,
this allocator object returns an error message of "memory leaked", and also,
a stack trace showing the exact point where the memory was leaked.

```{zig}
Expand Down Expand Up @@ -180,9 +177,9 @@ specific errors in your functions. In other words, you write
a unit test that tries to assert if a specific function call
returns any error, or a specific type of error.

In C++ you would normally write this stye of unit test using, for example,
In C++ you would normally write this style of unit tests using, for example,
the functions `REQUIRE_THROWS()` or `CHECK_THROWS()` from the [`Catch2` test framework](https://github.com/catchorg/Catch2/tree/devel)[^catch2].
In the case of a Python project, you would use the
In the case of a Python project, you would probably use the
[`raises()` function from `pytest`](https://docs.pytest.org/en/7.1.x/reference/reference.html#pytest-raises)[^pytest].
While in Rust, you would probably use `assert_eq!()` in conjunction with `Err()`.

Expand All @@ -195,7 +192,7 @@ With this function, you can test if a specific function call returns the exact
type of error that you expect it to return. To use this function, you first write
`try expectError()`. Then, on the first argument, you provide the type of error that you
are expecting from the function call. Then, on the second argument, you write
the function call you expect to fail.
the function call that you expect to fail.

The code example below demonstrates such type of unit test in Zig.
Notice that, inside the function `alloc_error()` we are allocating
Expand Down Expand Up @@ -245,7 +242,7 @@ All 1 tests passed.
In Zig, there are some different ways you can test for an equality.
You already saw that we can use `expect()` with the logical operator `==`
to essentially reproduce an equality test. But we also have
some helper functions that you should know about, especially
some other helper functions that you should know about, especially
`expectEqual()`, `expectEqualSlices()` and `expectEqualStrings()`.


Expand All @@ -254,7 +251,7 @@ test equality function. It receives two objects as input. The first
object is the value that you expect to be in the second object.
While second object is the object you have, or, the object that your application
produced as result. So, with `expectEqual()` you are essentially
testing if the values stored inside the two provided objects
testing if the values stored inside these two objects
are equal or not.

You can see in the example below that, the test performed by
Expand Down Expand Up @@ -313,9 +310,9 @@ All 1 tests passed.
At last, you might also want to use the `expectEqualStrings()` function.
As the name suggests, you can use this function to test if two strings
are equal or not. Just provide the two string objects that you want to compare,
as inputs to the functions.
as inputs to the function.

If the function finds any existing difference between the two strings,
If the function finds any existing differences between the two strings,
then, the function will raise an error, and also, print an error message
that shows the exact difference between the two string objects provided,
as the example below demonstrates:
Expand Down
4 changes: 2 additions & 2 deletions _freeze/Chapters/01-memory/execute-results/html.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions _freeze/Chapters/03-structs/execute-results/html.json

Large diffs are not rendered by default.

Loading

0 comments on commit 28c7d10

Please sign in to comment.