1
0
mirror of https://github.com/ThrowTheSwitch/Unity.git synced 2026-01-23 08:25:58 +01:00

Documentation improvements

* Fixed a broken markdown bulleted list
* Replaced a missing document link (from the original source of this documentation) with a full sentence explaining the relation of `assert()` to static analysis.
* Typographic fixes
  * Replaced single and double straight quotes with smart quotes where appropriate
  * Replaced three periods with ellipses where appropriate
This commit is contained in:
Mike Karlesky
2023-06-12 09:58:19 -04:00
parent bbb8b3f562
commit 4d64a17027

View File

@@ -10,46 +10,46 @@ Upon boolean False, an assertion stops execution and reports the failure.
and easily execute those assertions. and easily execute those assertions.
- The structure of Unity allows you to easily separate test assertions from - The structure of Unity allows you to easily separate test assertions from
source code in, well, test code. source code in, well, test code.
- Unity's assertions: - Unitys assertions:
- Come in many, many flavors to handle different C types and assertion cases. - Come in many, many flavors to handle different C types and assertion cases.
- Use context to provide detailed and helpful failure messages. - Use context to provide detailed and helpful failure messages.
- Document types, expected values, and basic behavior in your source code for - Document types, expected values, and basic behavior in your source code for
free. free.
### Unity Is Several Things But Mainly It's Assertions ### Unity Is Several Things But Mainly Its Assertions
One way to think of Unity is simply as a rich collection of assertions you can One way to think of Unity is simply as a rich collection of assertions you can
use to establish whether your source code behaves the way you think it does. use to establish whether your source code behaves the way you think it does.
Unity provides a framework to easily organize and execute those assertions in Unity provides a framework to easily organize and execute those assertions in
test code separate from your source code. test code separate from your source code.
### What's an Assertion? ### Whats an Assertion?
At their core, assertions are an establishment of truth - boolean truth. Was this At their core, assertions are an establishment of truth - boolean truth. Was this
thing equal to that thing? Does that code doohickey have such-and-such property thing equal to that thing? Does that code doohickey have such-and-such property
or not? You get the idea. Assertions are executable code (to appreciate the big or not? You get the idea. Assertions are executable code. Static analysis is a
picture on this read up on the difference between valuable approach to improving code quality, but it is not executing your code
[link:Dynamic Verification and Static Analysis]). A failing assertion stops in the way an assertion can. A failing assertion stops execution and reports an
execution and reports an error through some appropriate I/O channel (e.g. error through some appropriate I/O channel (e.g. stdout, GUI, output file,
stdout, GUI, file, blinky light). blinky light).
Fundamentally, for dynamic verification all you need is a single assertion Fundamentally, for dynamic verification all you need is a single assertion
mechanism. In fact, that's what the [assert() macro][] in C's standard library mechanism. In fact, thats what the [assert() macro][] in Cs standard library
is for. So why not just use it? Well, we can do far better in the reporting is for. So why not just use it? Well, we can do far better in the reporting
department. C's `assert()` is pretty dumb as-is and is particularly poor for department. Cs `assert()` is pretty dumb as-is and is particularly poor for
handling common data types like arrays, structs, etc. And, without some other handling common data types like arrays, structs, etc. And, without some other
support, it's far too tempting to litter source code with C's `assert()`'s. It's support, its far too tempting to litter source code with Cs `assert()`s. Its
generally much cleaner, manageable, and more useful to separate test and source generally much cleaner, manageable, and more useful to separate test and source
code in the way Unity facilitates. code in the way Unity facilitates.
### Unity's Assertions: Helpful Messages _and_ Free Source Code Documentation ### Unitys Assertions: Helpful Messages _and_ Free Source Code Documentation
Asserting a simple truth condition is valuable, but using the context of the Asserting a simple truth condition is valuable, but using the context of the
assertion is even more valuable. For instance, if you know you're comparing bit assertion is even more valuable. For instance, if you know youre comparing bit
flags and not just integers, then why not use that context to give explicit, flags and not just integers, then why not use that context to give explicit,
readable, bit-level feedback when an assertion fails? readable, bit-level feedback when an assertion fails?
That's what Unity's collection of assertions do - capture context to give you Thats what Unitys collection of assertions do - capture context to give you
helpful, meaningful assertion failure messages. In fact, the assertions helpful, meaningful assertion failure messages. In fact, the assertions
themselves also serve as executable documentation about types and values in your themselves also serve as executable documentation about types and values in your
source code. So long as your tests remain current with your source and all those source code. So long as your tests remain current with your source and all those
@@ -73,12 +73,12 @@ a simple null check).
- `Actual` is the value being tested and unlike the other parameters in an - `Actual` is the value being tested and unlike the other parameters in an
assertion construction is the only parameter present in all assertion variants. assertion construction is the only parameter present in all assertion variants.
- `Modifiers` are masks, ranges, bit flag specifiers, floating point deltas. - `Modifiers` are masks, ranges, bit flag specifiers, floating point deltas.
- `Expected` is your expected value (duh) to compare to an `actual` value; it's - `Expected` is your expected value (duh) to compare to an `actual` value; its
marked as an optional parameter because some assertions only need a single marked as an optional parameter because some assertions only need a single
`actual` parameter (e.g. null check). `actual` parameter (e.g. null check).
- `Size/count` refers to string lengths, number of array elements, etc. - `Size/count` refers to string lengths, number of array elements, etc.
Many of Unity's assertions are clear duplications in that the same data type Many of Unitys assertions are clear duplications in that the same data type
is handled by several assertions. The differences among these are in how failure is handled by several assertions. The differences among these are in how failure
messages are presented. For instance, a `_HEX` variant of an assertion prints messages are presented. For instance, a `_HEX` variant of an assertion prints
the expected and actual values of that assertion formatted as hexadecimal. the expected and actual values of that assertion formatted as hexadecimal.
@@ -99,7 +99,7 @@ _Example:_
TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} ) TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} )
``` ```
becomes messageified like thus... becomes messageified like thus
```c ```c
TEST_ASSERT_X_MESSAGE( {modifiers}, {expected}, actual, {size/count}, message ) TEST_ASSERT_X_MESSAGE( {modifiers}, {expected}, actual, {size/count}, message )
@@ -108,7 +108,7 @@ TEST_ASSERT_X_MESSAGE( {modifiers}, {expected}, actual, {size/count}, message )
Notes: Notes:
- The `_MESSAGE` variants intentionally do not support `printf` style formatting - The `_MESSAGE` variants intentionally do not support `printf` style formatting
since many embedded projects don't support or avoid `printf` for various reasons. since many embedded projects dont support or avoid `printf` for various reasons.
It is possible to use `sprintf` before the assertion to assemble a complex fail It is possible to use `sprintf` before the assertion to assemble a complex fail
message, if necessary. message, if necessary.
- If you want to output a counter value within an assertion fail message (e.g. from - If you want to output a counter value within an assertion fail message (e.g. from
@@ -119,7 +119,7 @@ Notes:
Unity provides a collection of assertions for arrays containing a variety of Unity provides a collection of assertions for arrays containing a variety of
types. These are documented in the Array section below. These are almost on par types. These are documented in the Array section below. These are almost on par
with the `_MESSAGE`variants of Unity's Asserts in that for pretty much any Unity with the `_MESSAGE`variants of Unitys Asserts in that for pretty much any Unity
type assertion you can tack on `_ARRAY` and run assertions on an entire block of type assertion you can tack on `_ARRAY` and run assertions on an entire block of
memory. memory.
@@ -144,7 +144,7 @@ Notes:
Unity provides a collection of assertions for arrays containing a variety of Unity provides a collection of assertions for arrays containing a variety of
types which can be compared to a single value as well. These are documented in types which can be compared to a single value as well. These are documented in
the Each Equal section below. these are almost on par with the `_MESSAGE` the Each Equal section below. these are almost on par with the `_MESSAGE`
variants of Unity's Asserts in that for pretty much any Unity type assertion you variants of Unitys Asserts in that for pretty much any Unity type assertion you
can inject `_EACH_EQUAL` and run assertions on an entire block of memory. can inject `_EACH_EQUAL` and run assertions on an entire block of memory.
```c ```c
@@ -203,7 +203,7 @@ code then verifies as a final step.
#### `TEST_PASS_MESSAGE("message")` #### `TEST_PASS_MESSAGE("message")`
This will abort the remainder of the test, but count the test as a pass. Under This will abort the remainder of the test, but count the test as a pass. Under
normal circumstances, it is not necessary to include this macro in your tests... normal circumstances, it is not necessary to include this macro in your tests
a lack of failure will automatically be counted as a `PASS`. It is occasionally a lack of failure will automatically be counted as a `PASS`. It is occasionally
useful for tests with `#ifdef`s and such. useful for tests with `#ifdef`s and such.
@@ -392,7 +392,7 @@ Asserts that the pointers point to the same memory location.
#### `TEST_ASSERT_EQUAL_STRING (expected, actual)` #### `TEST_ASSERT_EQUAL_STRING (expected, actual)`
Asserts that the null terminated (`'\0'`)strings are identical. If strings are Asserts that the null terminated (`\0`)strings are identical. If strings are
of different lengths or any portion of the strings before their terminators of different lengths or any portion of the strings before their terminators
differ, the assertion fails. Two NULL strings (i.e. zero length) are considered differ, the assertion fails. Two NULL strings (i.e. zero length) are considered
equivalent. equivalent.
@@ -561,7 +561,7 @@ Asserts that the `actual` value is NOT within +/- `delta` of the `expected` valu
#### `TEST_ASSERT_EQUAL_FLOAT (expected, actual)` #### `TEST_ASSERT_EQUAL_FLOAT (expected, actual)`
Asserts that the `actual` value is "close enough to be considered equal" to the Asserts that the `actual` value is close enough to be considered equal to the
`expected` value. If you are curious about the details, refer to the Advanced `expected` value. If you are curious about the details, refer to the Advanced
Asserting section for more details on this. Omitting a user-specified delta in a Asserting section for more details on this. Omitting a user-specified delta in a
floating point assertion is both a shorthand convenience and a requirement of floating point assertion is both a shorthand convenience and a requirement of
@@ -569,7 +569,7 @@ code generation conventions for CMock.
#### `TEST_ASSERT_NOT_EQUAL_FLOAT (expected, actual)` #### `TEST_ASSERT_NOT_EQUAL_FLOAT (expected, actual)`
Asserts that the `actual` value is NOT "close enough to be considered equal" to the Asserts that the `actual` value is NOT close enough to be considered equal to the
`expected` value. `expected` value.
#### `TEST_ASSERT_FLOAT_ARRAY_WITHIN (delta, expected, actual, num_elements)` #### `TEST_ASSERT_FLOAT_ARRAY_WITHIN (delta, expected, actual, num_elements)`
@@ -662,7 +662,7 @@ Asserts that the `actual` value is NOT within +/- `delta` of the `expected` valu
#### `TEST_ASSERT_EQUAL_DOUBLE (expected, actual)` #### `TEST_ASSERT_EQUAL_DOUBLE (expected, actual)`
Asserts that the `actual` value is "close enough to be considered equal" to the Asserts that the `actual` value is close enough to be considered equal to the
`expected` value. If you are curious about the details, refer to the Advanced `expected` value. If you are curious about the details, refer to the Advanced
Asserting section for more details. Omitting a user-specified delta in a Asserting section for more details. Omitting a user-specified delta in a
floating point assertion is both a shorthand convenience and a requirement of floating point assertion is both a shorthand convenience and a requirement of
@@ -670,7 +670,7 @@ code generation conventions for CMock.
#### `TEST_ASSERT_NOT_EQUAL_DOUBLE (expected, actual)` #### `TEST_ASSERT_NOT_EQUAL_DOUBLE (expected, actual)`
Asserts that the `actual` value is NOT "close enough to be considered equal" to the Asserts that the `actual` value is NOT close enough to be considered equal to the
`expected` value. `expected` value.
#### `TEST_ASSERT_DOUBLE_ARRAY_WITHIN (delta, expected, actual, num_elements)` #### `TEST_ASSERT_DOUBLE_ARRAY_WITHIN (delta, expected, actual, num_elements)`
@@ -753,7 +753,7 @@ Not A Number floating point representations.
This section helps you understand how to deal with some of the trickier This section helps you understand how to deal with some of the trickier
assertion situations you may run into. It will give you a glimpse into some of assertion situations you may run into. It will give you a glimpse into some of
the under-the-hood details of Unity's assertion mechanisms. If you're one of the under-the-hood details of Unitys assertion mechanisms. If youre one of
those people who likes to know what is going on in the background, read on. If those people who likes to know what is going on in the background, read on. If
not, feel free to ignore the rest of this document until you need it. not, feel free to ignore the rest of this document until you need it.
@@ -768,9 +768,9 @@ mathematical operations might result in a representation of 8 x 2-2
that also evaluates to a value of 2. At some point repeated operations cause that also evaluates to a value of 2. At some point repeated operations cause
equality checks to fail. equality checks to fail.
So Unity doesn't do direct floating point comparisons for equality. Instead, it So Unity doesnt do direct floating point comparisons for equality. Instead, it
checks if two floating point values are "really close." If you leave Unity checks if two floating point values are really close. If you leave Unity
running with defaults, "really close" means "within a significant bit or two." running with defaults, really close means within a significant bit or two.
Under the hood, `TEST_ASSERT_EQUAL_FLOAT` is really `TEST_ASSERT_FLOAT_WITHIN` Under the hood, `TEST_ASSERT_EQUAL_FLOAT` is really `TEST_ASSERT_FLOAT_WITHIN`
with the `delta` parameter calculated on the fly. For single precision, delta is with the `delta` parameter calculated on the fly. For single precision, delta is
the expected value multiplied by 0.00001, producing a very small proportional the expected value multiplied by 0.00001, producing a very small proportional
@@ -779,28 +779,27 @@ range around the expected value.
If you are expecting a value of 20,000.0 the delta is calculated to be 0.2. So If you are expecting a value of 20,000.0 the delta is calculated to be 0.2. So
any value between 19,999.8 and 20,000.2 will satisfy the equality check. This any value between 19,999.8 and 20,000.2 will satisfy the equality check. This
works out to be roughly a single bit of range for a single-precision number, and works out to be roughly a single bit of range for a single-precision number, and
that's just about as tight a tolerance as you can reasonably get from a floating thats just about as tight a tolerance as you can reasonably get from a floating
point value. point value.
So what happens when it's zero? Zero - even more than other floating point So what happens when its zero? Zero - even more than other floating point
values - can be represented many different ways. It doesn't matter if you have values - can be represented many different ways. It doesnt matter if you have
0 x 20 or 0 x 263.It's still zero, right? Luckily, if you 0x20 or 0x263. Its still zero, right? Luckily, if you subtract these
subtract these values from each other, they will always produce a difference of values from each other, they will always produce a difference of zero, which
zero, which will still fall between 0 plus or minus a delta of 0. So it still will still fall between 0 plus or minus a delta of 0. So it still works!
works!
Double precision floating point numbers use a much smaller multiplier, again Double precision floating point numbers use a much smaller multiplier, again
approximating a single bit of error. approximating a single bit of error.
If you don't like these ranges and you want to make your floating point equality If you dont like these ranges and you want to make your floating point equality
assertions less strict, you can change these multipliers to whatever you like by assertions less strict, you can change these multipliers to whatever you like by
defining UNITY_FLOAT_PRECISION and UNITY_DOUBLE_PRECISION. See Unity defining UNITY_FLOAT_PRECISION and UNITY_DOUBLE_PRECISION. See Unity
documentation for more. documentation for more.
### How do we deal with targets with non-standard int sizes? ### How do we deal with targets with non-standard int sizes?
It's "fun" that C is a standard where something as fundamental as an integer Its fun that C is a standard where something as fundamental as an integer
varies by target. According to the C standard, an `int` is to be the target's varies by target. According to the C standard, an `int` is to be the targets
natural register size, and it should be at least 16-bits and a multiple of a natural register size, and it should be at least 16-bits and a multiple of a
byte. It also guarantees an order of sizes: byte. It also guarantees an order of sizes:
@@ -814,7 +813,7 @@ and this remains perfectly standard C.
To make things even more interesting, there are compilers and targets out there To make things even more interesting, there are compilers and targets out there
that have a hard choice to make. What if their natural register size is 10-bits that have a hard choice to make. What if their natural register size is 10-bits
or 12-bits? Clearly they can't fulfill _both_ the requirement to be at least or 12-bits? Clearly they cant fulfill _both_ the requirement to be at least
16-bits AND the requirement to match the natural register size. In these 16-bits AND the requirement to match the natural register size. In these
situations, they often choose the natural register size, leaving us with situations, they often choose the natural register size, leaving us with
something like this: something like this:
@@ -823,24 +822,24 @@ something like this:
char (8 bit) <= short (12 bit) <= int (12 bit) <= long (16 bit) char (8 bit) <= short (12 bit) <= int (12 bit) <= long (16 bit)
``` ```
Um... yikes. It's obviously breaking a rule or two... but they had to break SOME Um yikes. Its obviously breaking a rule or two but they had to break SOME
rules, so they made a choice. rules, so they made a choice.
When the C99 standard rolled around, it introduced alternate standard-size types. When the C99 standard rolled around, it introduced alternate standard-size types.
It also introduced macros for pulling in MIN/MAX values for your integer types. It also introduced macros for pulling in MIN/MAX values for your integer types.
It's glorious! Unfortunately, many embedded compilers can't be relied upon to Its glorious! Unfortunately, many embedded compilers cant be relied upon to
use the C99 types (Sometimes because they have weird register sizes as described use the C99 types (Sometimes because they have weird register sizes as described
above. Sometimes because they don't feel like it?). above. Sometimes because they dont feel like it?).
A goal of Unity from the beginning was to support every combination of A goal of Unity from the beginning was to support every combination of
microcontroller or microprocessor and C compiler. Over time, we've gotten really microcontroller or microprocessor and C compiler. Over time, weve gotten really
close to this. There are a few tricks that you should be aware of, though, if close to this. There are a few tricks that you should be aware of, though, if
you're going to do this effectively on some of these more idiosyncratic targets. youre going to do this effectively on some of these more idiosyncratic targets.
First, when setting up Unity for a new target, you're going to want to pay First, when setting up Unity for a new target, youre going to want to pay
special attention to the macros for automatically detecting types special attention to the macros for automatically detecting types
(where available) or manually configuring them yourself. You can get information (where available) or manually configuring them yourself. You can get information
on both of these in Unity's documentation. on both of these in Unitys documentation.
What about the times where you suddenly need to deal with something odd, like a What about the times where you suddenly need to deal with something odd, like a
24-bit `int`? The simplest solution is to use the next size up. If you have a 24-bit `int`? The simplest solution is to use the next size up. If you have a
@@ -848,9 +847,9 @@ What about the times where you suddenly need to deal with something odd, like a
`int`, configure Unity to use 16 bits. There are two ways this is going to `int`, configure Unity to use 16 bits. There are two ways this is going to
affect you: affect you:
1. When Unity displays errors for you, it's going to pad the upper unused bits 1. When Unity displays errors for you, its going to pad the upper unused bits
with zeros. with zeros.
2. You're going to have to be careful of assertions that perform signed 2. Youre going to have to be careful of assertions that perform signed
operations, particularly `TEST_ASSERT_INT_WITHIN`. Such assertions might wrap operations, particularly `TEST_ASSERT_INT_WITHIN`. Such assertions might wrap
your `int` in the wrong place, and you could experience false failures. You can your `int` in the wrong place, and you could experience false failures. You can
always back down to a simple `TEST_ASSERT` and do the operations yourself. always back down to a simple `TEST_ASSERT` and do the operations yourself.