## Prerequisites
- Installed Ceedling Build tool
- Create Project [Getting Familiar With Project Structure.md](https://hackmd.io/IAMU_83wRuyV-17iH5NPBw)
- Source Code
```c
#include "vectorDotProduct.h"
int dotProduct(int vect_A[], int vect_B[])
{
int product = 0;
// Loop for calculate dot product
for (int i = 0; i < n; i++)
product = product + vect_A[i] * vect_B[i];
return product;
}
```
Header
```c
#ifndef VECTORDOTPRODUCT_H
#define VECTORDOTPRODUCT_H
int dotProduct(int vect_A[], int vect_B[]);
#endif // VECTORDOTPRODUCT_H
```
---
## Breakdown `test_vectorDotProduct.c`
The test file, as we agreed before, will be at `test/test_vectorDotProduct.c`.
When we create our module using `ceedling module:create[vectorDotProduct]`, it automatically creates the `test_vectorDotProduct.c` file, which contains:
```c
#ifdef TEST
#include "unity.h"
#include "vectorDotProduct.h"
void setUp(void)
{
}
void tearDown(void)
{
}
void test_vectorDotProduct_NeedToImplement(void)
{
TEST_IGNORE_MESSAGE("Need to Implement vectorDotProduct");
}
#endif // TEST
```
### Lets Breakdown the file content together.
---
### Guard Macros
For the following:
```c
#ifdef TEST
...
...
...
...
#endif // TEST
```
- By default, the `project.yml` defines the `TEST` macro when building the project.
- You should know that the `TEST` macro is defined by the Ceedling build tool.

- We can also define other macros by adding our macro names to `common_defines[]`.
- For example, if we need to define a `UNIT_TEST` macro, we can add it by modifying:
```yml
:defines:
:common: &common_defines [UNIT_TEST]
:test:
- *common_defines
- TEST
:test_preprocess:
- *common_defines
- TEST
```
Or add it here:
```yml
:defines:
:common: &common_defines []
:test:
- *common_defines
- TEST
- UNIT_TEST # <--- here
:test_preprocess:
- *common_defines
- TEST
- UNIT_TEST # <--- here
```
So, take care of these defines during the development phase to avoid any overlap between macros defined for testing and those defined for development configurations. We will use this feature when the project becomes more complex.
---
### Breakdown of Included Files
- `#include "unity.h"`: This is our testing framework's header file, which contains:
- Function declarations for `setUp` and `tearDown`.
- Configuration options.
- Test assert macros.
- And more. Please go through this file.
- `#include "vectorDotProduct.h"`: This is our module's header file that contains the public function we will test.
---
### The `setUp()` and `tearDown()` Functions
- These functions are called before and after each test in the test suite.
- The execution sequence is:
```c
setUp();
test_case1();
tearDown();
```
This saves time from having to copy/paste/edit from another test file.
---
### Prewritten Test Case
- Each test case must start with `test_`. Let's analyze the prewritten test case:
```c
void test_vectorDotProduct_NeedToImplement(void)
{
TEST_IGNORE_MESSAGE("Need to Implement vectorDotProduct");
}
```
- The test case name starts with `test_`, followed by the test case name.
- Inside the test case, the template ignores the test case, so anything after `TEST_IGNORE_MESSAGE` will not be executed.
- Delete this test case to write your own.
---
## Writing Our Own Test Case
- We design a component that calculates the dot product of two vectors.
- The dot product can indicate the alignment of two vectors:
- A higher value indicates greater alignment.
- A lower value indicates less alignment.
To write a test case, you need the requirements or detailed design for this module. For simplicity, we'll use the dot product example.
---
### Function API
In `vectorDotProduct.h`:
```c
int dotProduct(int vect_A[], int vect_B[]);
```
- The function takes two integer arrays as input (vectors A and B).
- It returns an integer result.
---
### Writing a Happy Scenario Test Case
1. Start with `test_`.
2. Write test data and call the function.
3. Assert the result to ensure it works as expected.
```c
void test_NormalVectors(void)
{
// Define the test vectors
int A[3] = {1, 2, 3};
int B[3] = {4, 5, 6};
// Call the function
int result = dotProduct(A, B);
// Assert the result
TEST_ASSERT_EQUAL(32, result);
}
```
#### Breakdown of the Function
- As discussed, any test function should start with `test_`, so we named it `test_NormalVectors`.
- Define two arrays for two vectors.
- Call the `dotProduct` function and save the result of the function return.
#### For Assertions
- Different macros are used for different assertions:
- **Boolean-Based Assertion**: Example: `TEST_ASSERT(condition)`
- **Integers-Based Assertion**: Example: `TEST_ASSERT_EQUAL_INT(expected, actual)`
- **Integer Ranges-Based Assertion**: Example: `TEST_ASSERT_INT_WITHIN(delta, expected, actual)`
- **Structs and Strings-Based Assertion**:
- Example: `TEST_ASSERT_EQUAL_PTR(expected, actual)`
- Example: `TEST_ASSERT_EQUAL_STRING(expected, actual)`
- There are many assertion types, which you can find in [unity.h](https://github.com/ThrowTheSwitch/Unity/blob/master/src/unity.h) or on the [Unity Assertions CheatSheet](https://github.com/ThrowTheSwitch/Unity/blob/master/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf).
- In this example, we will use the shorthand `TEST_ASSERT_EQUAL(expected, actual)`:
- Set the actual result as `20`, based on our previous calculation.
- The expected value is the function's return.
#### Run Test Cases
```bash
$> ceedling test:all
```
```bash
Test 'test_vectorDotProduct.c'
------------------------------
Generating runner for test_vectorDotProduct.c...
Compiling test_vectorDotProduct_runner.c...
Compiling test_vectorDotProduct.c...
Linking test_vectorDotProduct.out...
Running test_vectorDotProduct.out...
--------------------
OVERALL TEST SUMMARY
--------------------
TESTED: 1
PASSED: 1
FAILED: 0
IGNORED: 0
```
- We need more details about the test cases executed in this test runner, so we can add more details using Unity macros like `TEST_PASS_MESSAGE`.
```c
void test_NormalVectors(void)
{
// Define the first vector
int vect_A[3] = {2, 5, 4};
int vect_B[3] = {3, 2, 1};
// Call the function
int result = dotProduct(vect_A, vect_B);
// Make assertions
TEST_ASSERT_EQUAL(result, 20);
// Executed if the test passes
TEST_PASS_MESSAGE("TEST_PASS.");
}
```
#### Output
```bash
$> ceedling test:all
```
```bash
Test 'test_vectorDotProduct.c'
------------------------------
Generating runner for test_vectorDotProduct.c...
Compiling test_vectorDotProduct_runner.c...
Compiling test_vectorDotProduct.c...
Linking test_vectorDotProduct.out...
Running test_vectorDotProduct.out...
-----------
TEST OUTPUT
-----------
[test_vectorDotProduct.c]
- "test_vectorDotProduct.c:26:test_NormalVectors:INFO: TEST_PASS."
--------------------
OVERALL TEST SUMMARY
--------------------
TESTED: 1
PASSED: 1
FAILED: 0
IGNORED: 0
```
### Null Based Test Case
- This test case aims to see the component behavior when the first vector is a NULL pointer.
```c
void test_NullVector_A(void)
{
// Define the first vector
int vect_B[3] = {3, 2, 1};
// Call the function
int result = dotProduct(NULL, vect_B);
// Make assertions
TEST_ASSERT_EQUAL(result, 20);
// Executed if the test passes
TEST_PASS_MESSAGE("TEST_PASS.");
}
```
```bash
$> ceedling test:all
```
> Output
```
Test 'test_vectorDotProduct.c'
------------------------------
Generating runner for test_vectorDotProduct.c...
Compiling test_vectorDotProduct_runner.c...
Compiling test_vectorDotProduct.c...
Linking test_vectorDotProduct.out...
Running test_vectorDotProduct.out...
ERROR: Test executable "test_vectorDotProduct.out" failed.
> Produced no output to $stdout.
> And exited with status: [0] (count of failed tests).
> This is often a symptom of a bad memory access in source or test code.
rake aborted!
```
This error happens because our program doesn't handle any NULLs for any vectors, so to deal with that we have two solutions:
1. If you are the developer responsible for that component, you can edit your source code to handle NULL situations.
2. If you are responsible for testing only, you can make a bug report and ignore these test cases for now to run the rest of the test cases: TEST_IGNORE_MESSAGE("NULL Pointers Not Handled");
## It is your turn
You can add more test cases to handle different scenarios, such as testing for a zero vector, null pointer, and so on.