├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── inc └── vcan.h ├── src └── vcan.c └── tst ├── atto.c ├── atto.h └── test.c /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-*/ 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | =============================================================================== 3 | 4 | All notable changes to this project will be documented in this file. 5 | 6 | The format is based on 7 | [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 8 | and this project adheres to 9 | [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 10 | 11 | ******************************************************************************* 12 | 13 | [2.0.0] - 2020-04-22 14 | ---------------------------------------- 15 | 16 | Performance improvement, which breaks compatibility, although slightly. 17 | 18 | 19 | ### Modified 20 | 21 | - The message is not copied into the node anymore, it is removed from the 22 | `vcan_node_t` struct. Instead it is copied once and only once into the 23 | `vcan_bus_t` struct, to avoid copying it into every single node. 24 | - Due to the upper point, the callbacks now obtain also a const pointer to the 25 | message as a second parameter. 26 | 27 | 28 | ### Fixed 29 | 30 | - Minor doxygen documentation improvements. 31 | 32 | 33 | 34 | [1.0.0] - 2020-04-21 35 | ---------------------------------------- 36 | 37 | Initial version. 38 | 39 | 40 | ### Added 41 | 42 | - Connect/disconnect virtual nodes to/from bus. 43 | - Transmit message on bus, every node gets a copy and its callback is called. 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(VCAN 3 | VERSION 0.1.0 4 | LANGUAGES C 5 | DESCRIPTION 6 | "Virtual CAN bus") 7 | 8 | # Unless specified, by default create Release builds 9 | if (NOT CMAKE_BUILD_TYPE) 10 | set(CMAKE_BUILD_TYPE Release) 11 | endif () 12 | 13 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 14 | 15 | set(CMAKE_C_STANDARD 11) 16 | # Activate a million warnings to have the cleanest possible code 17 | set(FLAGS_WARNINGS -Wall -Wextra -pedantic -Wconversion -Wdouble-promotion 18 | -Wswitch-default -Wswitch-enum -Wuninitialized -Wno-unused-variable 19 | -Wpacked -Wpadded -Wshadow -Wformat-security -Wlogical-not-parentheses 20 | -Waggregate-return -Wmissing-declarations -Wmissing-declarations) 21 | # Debug build: compile with no optimisation, debug info and printing 22 | set(CMAKE_C_FLAGS_DEBUG "${WARNING_FLAGS} -g -O0 -DDEBUG") 23 | # Append sanitiser flags on non-Windows systems 24 | if (NOT WIN32 AND NOT CYGWIN AND NOT MSYS) 25 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} \ 26 | -fsanitize=address,undefined -static-libsan") 27 | endif () 28 | 29 | # Mini-sized release build: compile with optimisation for size 30 | # convert warnings into errors and some other optimisations 31 | set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} \ 32 | ${WARNING_FLAGS} \ 33 | -Os -Werror -fomit-frame-pointer -march=native -mtune=native") 34 | 35 | # Performance-oriented release build: compile with optimisation for speed 36 | # convert warnings into errors and some other optimisations 37 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} \ 38 | ${WARNING_FLAGS} \ 39 | -O3 -Werror -fomit-frame-pointer -march=native -mtune=native \ 40 | -funroll-loops") 41 | 42 | include_directories(inc/) 43 | set(LIB_FILES src/vcan.c) 44 | include_directories(tst/) 45 | set(TEST_FILES tst/test.c tst/atto.c) 46 | 47 | add_library("vcan${BITS}" STATIC ${LIB_FILES}) 48 | add_executable("testvcan${BITS}" ${LIB_FILES} ${TEST_FILES}) 49 | 50 | # Doxygen documentation builder 51 | find_package(Doxygen) 52 | if (DOXYGEN_FOUND) 53 | # Cmake's wrapper of Doxygen, constructing a doxyfile from the 54 | # DOXYGEN_* variables, which are mapped to the Doxygen variables. 55 | set(DOXYGEN_GENERATE_HTML YES) 56 | set(DOXYGEN_GENERATE_MAN YES) 57 | set(DOXYGEN_JAVADOC_AUTOBRIEF YES) 58 | set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES) 59 | set(DOXYGEN_SORT_MEMBER_DOCS NO) 60 | set(DOXYGEN_ALIASES license="**License:**") 61 | set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md) 62 | doxygen_add_docs(doxygen 63 | ALL # Build doxygen on make-all 64 | # List of input files for Doxygen 65 | ${PROJECT_SOURCE_DIR}/inc/vcan.h 66 | ${PROJECT_SOURCE_DIR}/LICENSE.md 67 | ${PROJECT_SOURCE_DIR}/README.md 68 | ${PROJECT_SOURCE_DIR}/CHANGELOG.md) 69 | else (DOXYGEN_FOUND) 70 | message(WARNING "Doxygen not found. Cannot generate documentation.") 71 | endif (DOXYGEN_FOUND) 72 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | ============================================================================== 3 | 4 | Copyright © 2020, Matjaž Guštin . 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS “AS IS” AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 27 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 28 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VCAN is a tiny virtual CAN and CAN-FD bus in C 2 | =============================================================================== 3 | 4 | Especially useful for debugging and testing without using actual 5 | CAN-connected devices, VCAN is a tiny C library that allows the user to connect 6 | virtual nodes on a virtual bus and make them react whenever someone transmits a 7 | message on the bus. 8 | 9 | After the transmission, each node obtains a copy of the message 10 | and a callback on each node is called to warn the node of a message being 11 | received. 12 | 13 | 14 | 15 | Known limitations 16 | ---------------------------------------- 17 | 18 | VCAN is simple, synchronous and single-threaded. It does not simulate 19 | transmission errors, collisions, arbitration, etc. just pure data transfer. 20 | Callbacks should be fast. 21 | 22 | ... but you are free to alter it to your specific needs! 23 | 24 | 25 | 26 | Usage example 27 | ---------------------------------------- 28 | 29 | ```c 30 | // Create and initialise a virtual bus. 31 | vcan_bus_t bus; 32 | vcan_err_t err; 33 | err = vcan_init(&bus); // Fails on NULL args 34 | assert(err == VCAN_OK); 35 | 36 | // Create 3 virtual nodes. 37 | // Make them print the received message on reception using the callback. 38 | // The definition of callback_print_msg() is available in the tst/test.c file 39 | vcan_node_t node1 = { 40 | .id = 1, 41 | .callback_on_rx = callback_print_msg, 42 | }; 43 | vcan_node_t node2 = { 44 | .id = 2, 45 | .callback_on_rx = callback_print_msg, 46 | }; 47 | vcan_node_t node3 = { 48 | .id = 3, 49 | .callback_on_rx = callback_print_msg, 50 | }; 51 | 52 | // Connect the nodes to the bus - by default up to 16 nodes. 53 | // It's just a #define constant, it can be changed easily. 54 | err = vcan_connect(&bus, &node1); // Fails on NULL args or full bus 55 | assert(err == VCAN_OK); 56 | err = vcan_connect(&bus, &node2); // Fails on NULL args or full bus 57 | assert(err == VCAN_OK); 58 | err = vcan_connect(&bus, &node3); // Fails on NULL args or full bus 59 | assert(err == VCAN_OK); 60 | 61 | // Transmit! 62 | puts("Transmitting from node 1, node 2 and 3 receive."); 63 | const vcan_msg_t msg = { 64 | .id = 0xABCD, 65 | .len = 3, 66 | .data = {0x00, 0x1A, 0x2B} 67 | }; 68 | err = vcan_tx(&bus, &msg, &node1); // Fails on NULL bus or NULL msg 69 | assert(err == VCAN_OK); 70 | 71 | // Because we transmitted from node 1, only node 2 and node 3 received the 72 | // message. Of course node 1 did not receive it, because it sent it. 73 | // The callbacks were triggered immediately and your stdout should 74 | // look like this: 75 | // 76 | // Transmitting from node 1, node 2 and 3 receive. 77 | // Node 2 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 78 | // Node 3 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 79 | 80 | // If you transmit from a NULL node, then all nodes receive the message. 81 | puts("Transmitting from NULL node, everyone receives."); 82 | err = vcan_tx(&bus, &msg, NULL); 83 | assert(err == VCAN_OK); 84 | 85 | // And now the stdout should look like this: 86 | // 87 | // Transmitting from NULL node, everyone receives. 88 | // Node 1 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 89 | // Node 2 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 90 | // Node 3 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 91 | 92 | // Disconnecting nodes is easy. 93 | err = vcan_disconnect(&bus, &node2); // Fails on NULL args or already 94 | // disconnected node 95 | assert(err == VCAN_OK); 96 | 97 | // Now node2 will not receive anything anymore. 98 | puts("Transmitting from node 1 after node 2 disconnection," 99 | " node 3 receives."); 100 | err = vcan_tx(&bus, &msg, &node1); 101 | assert(err == VCAN_OK); 102 | 103 | // And now the stdout should look like this: 104 | // 105 | // Transmitting from node 1 after node 2 disconnection, node 3 receives. 106 | // Node 3 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 107 | ``` 108 | 109 | You can also check the `tst/test.c` file for more examples. 110 | 111 | 112 | 113 | Include it in your project 114 | ---------------------------------------- 115 | 116 | ### Static source inclusion 117 | 118 | Copy the `inc/vcan.h` and `src/vcan.c` files into your existing 119 | C project, add them to the source folders and compile. Done. 120 | 121 | 122 | 123 | ### Compiling into all possible targets 124 | 125 | ``` 126 | mkdir build && cd build 127 | cmake .. -DCMAKE_BUILD_TYPE=Release 128 | cmake --build . 129 | ``` 130 | 131 | This will build all targets: 132 | 133 | - a `libvcan.a` static library 134 | - a test runner executable `testvcan` 135 | - the Doxygen documentation (if Doxygen is installed) 136 | 137 | To compile with the optimisation for size, use the 138 | `-DCMAKE_BUILD_TYPE=MinSizeRel` flag instead. 139 | -------------------------------------------------------------------------------- /inc/vcan.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * VCAN is a tiny Virtual CAN and CAN-FD bus. 5 | * 6 | * Especially useful for debugging and testing without using actual 7 | * CAN-connected devices, VCAN is a tiny C library that allows the user to 8 | * connect virtual nodes on a virtual bus and make them react whenever 9 | * someone transmits a message on the bus. 10 | * 11 | * After the transmission, each node obtains a copy of the message 12 | * and a callback on each node is called to warn the node of a message being 13 | * received. 14 | * 15 | * **Limitations** 16 | * 17 | * VCAN is simple, synchronous and not thread safe. It does not simulate 18 | * transmission errors, collisions, arbitration, etc. just pure data transfer. 19 | * Callbacks should be fast. 20 | * 21 | * ... but you are free to alter it to your specific needs! 22 | * 23 | * @copyright Copyright © 2020, Matjaž Guštin 24 | * . All rights reserved. 25 | * @license BSD 3-clause license. 26 | */ 27 | 28 | #ifndef VCAN_H 29 | #define VCAN_H 30 | 31 | #ifdef __cplusplus 32 | extern "C" 33 | { 34 | #endif 35 | 36 | /** VCAN version using semantic versioning. */ 37 | #define VCAN_VERSION "2.0.0" 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | /** Max payload size of a CAN message in bytes. */ 45 | #define VCAN_DATA_MAX_LEN 64 46 | /** MAx amount of virtual nodes connected to the virtual bus. */ 47 | #define VCAN_MAX_CONNECTED_NODES 16 48 | 49 | /** VCAN error codes. */ 50 | typedef enum 51 | { 52 | /** Successfully completed. */ 53 | VCAN_OK = 0, 54 | /** The bus argument is NULL. */ 55 | VCAN_NULL_BUS = 1, 56 | /** The message argument is NULL. */ 57 | VCAN_NULL_MSG = 2, 58 | /** The node argument is NULL. */ 59 | VCAN_NULL_NODE = 3, 60 | /** The callback within the node is NULL. */ 61 | VCAN_NULL_CALLBACK = 4, 62 | 63 | /** 64 | * Max amount of connected nodes reached. 65 | * Consider increasing #VCAN_MAX_CONNECTED_NODES. 66 | */ 67 | VCAN_TOO_MANY_CONNECTED = 5, 68 | 69 | /** This node is not connected to the bus, so it cannot be disconnected. */ 70 | VCAN_NODE_NOT_FOUND = 6, 71 | /** The node is already connected to the bus. */ 72 | VCAN_ALREADY_CONNECTED = 7, 73 | } vcan_err_t; 74 | 75 | /** Message to transmit or receive. */ 76 | typedef struct 77 | { 78 | /** The CAN ID - unused by VCAN. */ 79 | uint32_t id; 80 | 81 | /** Used bytes in the \p data field. */ 82 | uint32_t len; 83 | 84 | /** Payload. */ 85 | uint8_t data[VCAN_DATA_MAX_LEN]; 86 | } vcan_msg_t; 87 | 88 | /** 89 | * Virtual node. 90 | * 91 | * Contains a copy of the last received message and a callback function, which 92 | * is called whenever a message is received. 93 | */ 94 | struct vcan_node 95 | { 96 | /** 97 | * Callback called when a message has been written into \p received_msg. 98 | * 99 | * NOTE: the callback should be simple and fast, e.g. copying the message 100 | * somewhere else and triggering a flag. 101 | * 102 | * @param node the address of this node. The whole node itself is passed 103 | * to the callback, so the function has access to itself (for recursion), 104 | * to any custom data it may need and of course to the just received 105 | * message. 106 | */ 107 | void (* callback_on_rx)(struct vcan_node* node, const vcan_msg_t* msg); 108 | 109 | /** Any data the callback may need, such as a flag to trigger on 110 | * reception. Can be NULL. */ 111 | void* other_custom_data; 112 | 113 | /** Identifier of the node. Can be set to anything, VCAN does not use it. */ 114 | uint32_t id; 115 | }; 116 | 117 | /** 118 | * Virtual node. 119 | * 120 | * Contains a copy of the last received message and a callback function, which 121 | * is called whenever a message is received. 122 | */ 123 | typedef struct vcan_node vcan_node_t; 124 | 125 | /** 126 | * Virtual bus. 127 | * 128 | * Contains a list of nodes connected to it. 129 | */ 130 | typedef struct 131 | { 132 | /** The message just transmitted over the bus. */ 133 | vcan_msg_t received_msg; 134 | 135 | /** Nodes to deliver new messages to. */ 136 | vcan_node_t* nodes[VCAN_MAX_CONNECTED_NODES]; 137 | 138 | /** Amount of nodes. */ 139 | size_t connected; 140 | } vcan_bus_t; 141 | 142 | /** 143 | * Initialises the bus. 144 | * 145 | * @param bus not NULL 146 | * @return 147 | * - #VCAN_NULL_BUS on \p bus being NULL 148 | * - #VCAN_OK otherwise 149 | */ 150 | vcan_err_t vcan_init(vcan_bus_t* bus); 151 | 152 | /** 153 | * Attaches a new node to the bus, enabling it to receive any transmitted 154 | * message. 155 | * 156 | * When someone transmits a message, the node will get a copy into 157 | * \p node->received_msg and its callback will be called, passing 158 | * the node itself as its only argument. 159 | * 160 | * @param bus not NULL 161 | * @param node not NULL, with callback not NULL 162 | * @return 163 | * - #VCAN_NULL_BUS on \p bus being NULL 164 | * - #VCAN_NULL_NODE on \p node being NULL 165 | * - #VCAN_NULL_CALLBACK on \p node->callback_on_rx being NULL 166 | * - #VCAN_TOO_MANY_CONNECTED when there number of already connected nodes 167 | * to the bus the maximum. Increase #VCAN_MAX_CONNECTED_NODES if required. 168 | * - #VCAN_ALREADY_CONNECTED when the node is already connected to the bus, 169 | * there is nothing to be done. 170 | * - #VCAN_OK otherwise 171 | */ 172 | vcan_err_t vcan_connect(vcan_bus_t* bus, vcan_node_t* node); 173 | 174 | /** 175 | * Detaches a node from the bus, disabling it from receiving any further 176 | * messages. 177 | * 178 | * @param bus not NULL 179 | * @param node not NULL 180 | * @return 181 | * - #VCAN_NULL_BUS on \p bus being NULL 182 | * - #VCAN_NULL_NODE on \p node being NULL 183 | * - #VCAN_NODE_NOT_FOUND the node was not connected to this bus, so there 184 | * is nothing to do 185 | * - #VCAN_OK otherwise 186 | */ 187 | vcan_err_t vcan_disconnect(vcan_bus_t* bus, const vcan_node_t* node); 188 | 189 | /** 190 | * Sends a copy of the message to every connected node and calls every nodes's 191 | * callback to notify them. 192 | * 193 | * If you include a transmitting node, that one is excluded from the reception. 194 | * 195 | * The callbacks must be fast in order to make this function perform quick 196 | * enough. Before transmitting the next message, the user should take care 197 | * that each virtual node has finished processing the message (e.g. copying to 198 | * another location), so the next transmit does not overwrite the 199 | * unprocessed message in the nodes. 200 | * 201 | * @param bus not NULL 202 | * @param msg not NULL 203 | * @param src_node can be NULL 204 | * @return 205 | * - #VCAN_NULL_BUS on \p bus being NULL 206 | * - #VCAN_NULL_MSG on \p msg being NULL 207 | * - #VCAN_OK otherwise 208 | */ 209 | vcan_err_t vcan_tx(vcan_bus_t* bus, 210 | const vcan_msg_t* msg, 211 | const vcan_node_t* src_node); 212 | 213 | #ifdef __cplusplus 214 | } 215 | #endif 216 | 217 | #endif /* VCAN_H */ 218 | -------------------------------------------------------------------------------- /src/vcan.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * VCAN library implementation. 5 | * 6 | * @copyright Copyright © 2020, Matjaž Guštin 7 | * . All rights reserved. 8 | * @license BSD 3-clause license. 9 | */ 10 | 11 | #include "vcan.h" 12 | 13 | vcan_err_t vcan_init(vcan_bus_t* const bus) 14 | { 15 | vcan_err_t err; 16 | if (bus == NULL) 17 | { 18 | err = VCAN_NULL_BUS; 19 | } 20 | else 21 | { 22 | memset(bus, 0, sizeof(vcan_bus_t)); 23 | err = VCAN_OK; 24 | } 25 | return err; 26 | } 27 | 28 | vcan_err_t vcan_tx(vcan_bus_t* const bus, 29 | const vcan_msg_t* const msg, 30 | const vcan_node_t* const src_node) 31 | { 32 | vcan_err_t err; 33 | if (bus == NULL) 34 | { 35 | err = VCAN_NULL_BUS; 36 | } 37 | else if (msg == NULL) 38 | { 39 | err = VCAN_NULL_MSG; 40 | } 41 | else 42 | { 43 | memcpy(&bus->received_msg, msg, sizeof(vcan_msg_t)); 44 | for (size_t i = 0; i < bus->connected; i++) 45 | { 46 | if (bus->nodes[i] != src_node) 47 | { 48 | bus->nodes[i]->callback_on_rx(bus->nodes[i], &bus->received_msg); 49 | } 50 | } 51 | err = VCAN_OK; 52 | } 53 | return err; 54 | } 55 | 56 | vcan_err_t vcan_connect(vcan_bus_t* const bus, 57 | vcan_node_t* const node) 58 | { 59 | vcan_err_t err; 60 | if (bus == NULL) 61 | { 62 | err = VCAN_NULL_BUS; 63 | } 64 | else if (node == NULL) 65 | { 66 | err = VCAN_NULL_NODE; 67 | } 68 | else if (node->callback_on_rx == NULL) 69 | { 70 | err = VCAN_NULL_CALLBACK; 71 | } 72 | else if (bus->connected >= VCAN_MAX_CONNECTED_NODES) 73 | { 74 | err = VCAN_TOO_MANY_CONNECTED; 75 | } 76 | else 77 | { 78 | err = VCAN_OK; 79 | for (size_t i = 0; i < bus->connected; i++) 80 | { 81 | if (bus->nodes[i] == node) 82 | { 83 | err = VCAN_ALREADY_CONNECTED; 84 | } 85 | } 86 | if (err == VCAN_OK) 87 | { 88 | bus->nodes[bus->connected++] = node; 89 | } 90 | } 91 | return err; 92 | } 93 | 94 | vcan_err_t vcan_disconnect(vcan_bus_t* const bus, 95 | const vcan_node_t* const node) 96 | { 97 | vcan_err_t err; 98 | if (bus == NULL) 99 | { 100 | err = VCAN_NULL_BUS; 101 | } 102 | else if (node == NULL) 103 | { 104 | err = VCAN_NULL_NODE; 105 | } 106 | else 107 | { 108 | err = VCAN_NODE_NOT_FOUND; 109 | for (size_t i = 0; i < bus->connected; i++) 110 | { 111 | if (bus->nodes[i] == node) 112 | { 113 | const size_t nodes_to_shift = bus->connected - i - 1; 114 | memmove(&bus->nodes[i], 115 | &bus->nodes[i + 1], 116 | nodes_to_shift * sizeof(vcan_node_t*)); 117 | bus->connected--; 118 | err = VCAN_OK; 119 | break; 120 | } 121 | } 122 | } 123 | return err; 124 | } 125 | -------------------------------------------------------------------------------- /tst/atto.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * Atto - the microscopic C unit test framework 4 | * 5 | * @copyright Copyright © 2019-2020, Matjaž Guštin 6 | * . All rights reserved. 7 | * @license BSD 3-Clause License 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright notice, 15 | * this list of conditions and the following disclaimer in the documentation 16 | * and/or other materials provided with the distribution. 17 | * 3. Neither the name of nor the names of its contributors may be used to 18 | * endorse or promote products derived from this software without specific 19 | * prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS “AS IS” 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 25 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | char atto_at_least_one_fail = 0; 34 | -------------------------------------------------------------------------------- /tst/atto.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * Atto - the microscopic C unit test framework 4 | * 5 | * @copyright Copyright © 2019-2020, Matjaž Guštin 6 | * . All rights reserved. 7 | * @license BSD 3-Clause License 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright notice, 15 | * this list of conditions and the following disclaimer in the documentation 16 | * and/or other materials provided with the distribution. 17 | * 3. Neither the name of nor the names of its contributors may be used to 18 | * endorse or promote products derived from this software without specific 19 | * prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS “AS IS” 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 25 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef ATTO_H 34 | #define ATTO_H 35 | 36 | #ifdef __cplusplus 37 | extern "C" 38 | { 39 | #endif 40 | 41 | /** 42 | * Semantic version of this file and framework. 43 | */ 44 | #define ATTO_VERSION "1.2.0" 45 | 46 | #include /* For printf() */ 47 | #include /* For fabs(), fabsf(), isnan(), isinf(), isfinite() */ 48 | #include /* For strncmp(), memcmp() */ 49 | 50 | /** 51 | * Boolean indicating if all tests passed successfully (when 0) or not. 52 | * 53 | * Useful as a value to return from the main function of the test executable, 54 | * so that the test executable returns non-zero in case at least one test 55 | * failed. 56 | */ 57 | extern char atto_at_least_one_fail; 58 | 59 | /** 60 | * Absolute tolerance when comparing two single-precision floating point 61 | * value for approximate-equality using atto_fapprox(). 62 | * 63 | * If the difference between the two float values is bigger than this 64 | * tolerance, the assertion fails. 65 | */ 66 | #define ATTO_FLOAT_EQ_ABSTOL (1e-5f) 67 | 68 | /** 69 | * Absolute tolerance when comparing two double-precision floating point 70 | * value for approximate-equality using atto_dapprox(). 71 | * 72 | * If the difference between the two double values is bigger than this 73 | * tolerance, the assertion fails. 74 | */ 75 | #define ATTO_DOUBLE_EQ_ABSTOL (1e-8) 76 | 77 | /** 78 | * Verifies if the given boolean expression is true. 79 | * 80 | * Otherwise stops the test case immediately and reports the failing file, 81 | * function and line number on standard output. 82 | * 83 | * It's the most generic macro offered by Atto. If you need something more 84 | * specific, try using the other macros instead. 85 | * 86 | * The `do-while(0)` construct allows to write multi-line macros. 87 | * 88 | * If your system does not support `printf()`, replace it with something 89 | * else in this file! For example a `transmit()` function to communicate 90 | * the result to other devices. 91 | * 92 | * Example: 93 | * ``` 94 | * atto_assert(1); // Passes 95 | * atto_assert(0); // Fails 96 | * atto_assert(3 < 1); // Fails 97 | * ``` 98 | */ 99 | #define atto_assert(expression) do { \ 100 | if (!(expression)) { \ 101 | printf("FAIL | File: %s | Line: %4d | Test case: %s\n", \ 102 | __FILE__, __LINE__, __func__); \ 103 | atto_at_least_one_fail = 1U; \ 104 | return; \ 105 | } \ 106 | } while (0) 107 | 108 | /** 109 | * Verifies if the given boolean expression is false. 110 | * 111 | * Otherwise stops the test case immediately and reports the failing file, 112 | * function and line number on standard output. 113 | * 114 | * Example: 115 | * ``` 116 | * atto_false(1); // Fails 117 | * atto_false(0); // Passes 118 | * ``` 119 | */ 120 | #define atto_false(x) atto_assert(!(x)) 121 | 122 | /** 123 | * Verifies if the two arguments are exactly equal. 124 | * 125 | * Otherwise stops the test case immediately and reports the failing file, 126 | * function and line number on standard output. 127 | * 128 | * It is recommended to use this macro instead of calling `atto_assert(a == b)` 129 | * to avoid mistyping the equality comparison operator `==` into the assignment 130 | * operator `=`. 131 | * 132 | * Example: 133 | * ``` 134 | * atto_eq(12, 12); // Passes 135 | * atto_eq(12.0f, 12U); // Passes due to implicit conversion of 12U to 12.0f 136 | * atto_eq(100, 1); // Fails 137 | * ``` 138 | */ 139 | #define atto_eq(a, b) atto_assert((a) == (b)) 140 | 141 | /** 142 | * Verifies if the two arguments are not equal. 143 | * 144 | * Otherwise stops the test case immediately and reports the failing file, 145 | * function and line number on standard output. 146 | * 147 | * Example: 148 | * ``` 149 | * atto_neq(12, 12); // Fails 150 | * atto_neq(12.0f, 12U); // Fails 151 | * atto_neq(100, 1); // Passes 152 | * ``` 153 | */ 154 | #define atto_neq(a, b) atto_assert((a) != (b)) 155 | 156 | /** 157 | * Verifies if the first argument is strictly Greater Than the second. 158 | * 159 | * Otherwise stops the test case immediately and reports the failing file, 160 | * function and line number on standard output. 161 | * 162 | * Example: 163 | * ``` 164 | * atto_gt(1, 10); // Fails 165 | * atto_gt(10, 10); // Fails 166 | * atto_gt(100, 10); // Passes 167 | * ``` 168 | */ 169 | #define atto_gt(a, b) atto_assert((a) > (b)) 170 | 171 | /** 172 | * Verifies if the first argument is Greater or Equal to the second. 173 | * 174 | * Otherwise stops the test case immediately and reports the failing file, 175 | * function and line number on standard output. 176 | * 177 | * Example: 178 | * ``` 179 | * atto_ge(1, 10); // Fails 180 | * atto_ge(10, 10); // Passes 181 | * atto_ge(100, 10); // Passes 182 | * ``` 183 | */ 184 | #define atto_ge(a, b) atto_assert((a) >= (b)) 185 | 186 | /** 187 | * Verifies if the first argument is strictly Less Than the second. 188 | * 189 | * Otherwise stops the test case immediately and reports the failing file, 190 | * function and line number on standard output. 191 | * 192 | * Example: 193 | * ``` 194 | * atto_lt(1, 10); // Passes 195 | * atto_lt(10, 10); // Fails 196 | * atto_lt(100, 10); // Fails 197 | * ``` 198 | */ 199 | #define atto_lt(a, b) atto_assert((a) < (b)) 200 | 201 | /** 202 | * Verifies if the first argument is Less or Equal to the second. 203 | * 204 | * Otherwise stops the test case immediately and reports the failing file, 205 | * function and line number on standard output. 206 | * 207 | * Example: 208 | * ``` 209 | * atto_le(1, 10); // Passes 210 | * atto_le(10, 10); // Fails 211 | * atto_le(100, 10); // Fails 212 | * ``` 213 | */ 214 | #define atto_le(a, b) atto_assert((a) <= (b)) 215 | 216 | /** 217 | * Verifies if two single-precision floating point values are within a given 218 | * absolute tolerance from each other. 219 | * 220 | * Otherwise stops the test case and reports on standard output. 221 | * 222 | * Example: 223 | * ``` 224 | * atto_fdelta(1.0f, 1.00000001f, 0.1f); // Passes 225 | * atto_fdelta(1.0f, 1.1f, 0.15f); // Passes 226 | * atto_fdelta(1.0f, 2.0f, 0.1f); // Fails 227 | * ``` 228 | */ 229 | #define atto_fdelta(a, b, delta) atto_assert(fabsf((a) - (b)) <= fabsf(delta)) 230 | 231 | /** 232 | * Verifies if two single-precision floating point values are within a fixed 233 | * absolute tolerance #ATTO_FLOAT_EQ_ABSTOL from each other. 234 | * 235 | * Useful to check for almost-equality but ignoring minor rounding errors. 236 | * 237 | * Otherwise stops the test case and reports on standard output. 238 | * 239 | * Example: 240 | * ``` 241 | * atto_fapprox(1.0f, 1.000001f); // Passes 242 | * atto_fapprox(1.0f, 1.1); // Fails 243 | * ``` 244 | */ 245 | #define atto_fapprox(a, b) atto_fdelta((a), (b), ATTO_FLOAT_EQ_ABSTOL) 246 | 247 | /** 248 | * Verifies if two double-precision floating point values are within a given 249 | * absolute tolerance from each other. 250 | * 251 | * Otherwise stops the test case and reports on standard output. 252 | * 253 | * Example: 254 | * ``` 255 | * atto_ddelta(1.0, 1.00000001, 0.1); // Passes 256 | * atto_ddelta(1.0, 1.1, 0.15); // Passes 257 | * atto_ddelta(1.0, 2.0, 0.1); // Fails 258 | * ``` 259 | */ 260 | #define atto_ddelta(a, b, delta) \ 261 | atto_assert(fabs((a) - (b)) <= fabs(delta)) 262 | 263 | /** 264 | * Verifies if two double-precision floating point values are within a fixed 265 | * absolute tolerance #ATTO_DOUBLE_EQ_ABSTOL from each other. 266 | * 267 | * Useful to check for almost-equality but ignoring minor rounding errors. 268 | * 269 | * Otherwise stops the test case and reports on standard output. 270 | * 271 | * Example: 272 | * ``` 273 | * atto_dapprox(1.0, 1.00000001); // Passes 274 | * atto_dapprox(1.0, 1.1); // Fails 275 | * ``` 276 | */ 277 | #define atto_dapprox(a, b) atto_ddelta((a), (b), ATTO_DOUBLE_EQ_ABSTOL) 278 | 279 | /** 280 | * Verifies that the floating point value is Not a Number (NaN). 281 | * 282 | * Otherwise stops the test case and reports on standard output. 283 | * 284 | * Example: 285 | * ``` 286 | * atto_nan(NAN); // Passes 287 | * atto_nan(INFINITY); // Fails 288 | * atto_nan(-INFINITY); // Fails 289 | * atto_nan(1.0); // Fails 290 | * atto_nan(1); // Fails 291 | * ``` 292 | */ 293 | #define atto_nan(value) atto_assert(isnan(value)) 294 | 295 | /** 296 | * Verifies that the floating point value is infinity, either positive or 297 | * negative. 298 | * 299 | * Otherwise stops the test case and reports on standard output. 300 | * 301 | * Example: 302 | * ``` 303 | * atto_inf(NAN); // Fails 304 | * atto_inf(INFINITY); // Passes 305 | * atto_inf(-INFINITY); // Passes 306 | * atto_inf(1.0); // Fails 307 | * atto_inf(1); // Fails 308 | * ``` 309 | */ 310 | #define atto_inf(value) atto_assert(isinf(value)) 311 | 312 | /** 313 | * Verifies that the floating point value is positive infinity. 314 | * 315 | * Otherwise stops the test case and reports on standard output. 316 | * 317 | * Example: 318 | * ``` 319 | * atto_plusinf(NAN); // Fails 320 | * atto_plusinf(INFINITY); // Passes 321 | * atto_plusinf(-INFINITY); // Fails 322 | * atto_plusinf(1.0); // Fails 323 | * atto_plusinf(1); // Fails 324 | * ``` 325 | */ 326 | #define atto_plusinf(value) atto_assert((isinf(value)) && (value > 0)) 327 | 328 | /** 329 | * Verifies that the floating point value is negative infinity. 330 | * 331 | * Otherwise stops the test case and reports on standard output. 332 | * 333 | * Example: 334 | * ``` 335 | * atto_minusinf(NAN); // Fails 336 | * atto_minusinf(INFINITY); // Fails 337 | * atto_minusinf(-INFINITY); // Passes 338 | * atto_minusinf(1.0); // Fails 339 | * atto_minusinf(1); // Fails 340 | * ``` 341 | */ 342 | #define atto_minusinf(value) atto_assert((isinf(value)) && (value < 0)) 343 | 344 | /** 345 | * Verifies that the floating point value is finite, thus not NaN or 346 | * +/- infinity. 347 | * 348 | * Otherwise stops the test case and reports on standard output. 349 | * 350 | * Example: 351 | * ``` 352 | * atto_finite(NAN); // Fails 353 | * atto_finite(INFINITY); // Fails 354 | * atto_finite(-INFINITY); // Fails 355 | * atto_finite(1.0); // Passes 356 | * atto_finite(1); // Passes 357 | * ``` 358 | */ 359 | #define atto_finite(value) atto_assert(isfinite(value)) 360 | 361 | /** 362 | * Verifies that the floating point value is not finite, thus either NaN or 363 | * +/- infinity. 364 | * 365 | * Otherwise stops the test case and reports on standard output. 366 | * 367 | * Example: 368 | * ``` 369 | * atto_notfinite(NAN); // Passes 370 | * atto_notfinite(INFINITY); // Passes 371 | * atto_notfinite(-INFINITY); // Passes 372 | * atto_notfinite(1.0); // Fails 373 | * atto_notfinite(1); // Fails 374 | * ``` 375 | */ 376 | #define atto_notfinite(value) atto_assert(!isfinite(value)) 377 | 378 | /** 379 | * Verifies if the bits of the value specified by a bit mask are set to 1. 380 | * 381 | * Otherwise stops the test case and reports on standard output. 382 | * 383 | * Example: 384 | * ``` 385 | * atto_flag(0x07, 1 << 1); // Passes 386 | * atto_flag(0x07, 0x04); // Passes 387 | * atto_flag(0x07, 0x06); // Passes 388 | * atto_flag(0x07, 0xF0); // Fails 389 | * ``` 390 | */ 391 | #define atto_flag(value, mask) atto_assert(((value) & (mask))) 392 | 393 | /** 394 | * Verifies if the bits of the value specified by a bit mask are set to 0. 395 | * 396 | * Otherwise stops the test case and reports on standard output. 397 | * 398 | * Example: 399 | * ``` 400 | * atto_noflag(0x07, 1 << 5); // Passes 401 | * atto_noflag(0x07, 0xF8); // Passes 402 | * atto_noflag(0x07, 0x04); // Fails 403 | * ``` 404 | */ 405 | #define atto_noflag(value, mask) atto_assert(((value) & (mask)) == 0) 406 | 407 | /** 408 | * Verifies if two strings are equal up to a given length or until the shortest 409 | * string terminates. 410 | * 411 | * Otherwise stops the test case and reports on standard output. 412 | * 413 | * Example: 414 | * ``` 415 | * atto_streq("abcd", "abcd", 2); // Passes 416 | * atto_streq("abcd", "abcd", 4); // Passes 417 | * atto_streq("abcd", "abcd", 100); // Passes, reached null terminator 418 | * atto_streq("abcd", "ABCD", 4); // Fails, different casing 419 | * ``` 420 | */ 421 | #define atto_streq(a, b, maxlen) \ 422 | atto_assert(strncmp((a), (b), (maxlen)) == 0) 423 | 424 | /** 425 | * Verifies if two memory sections are equal up to a given length. 426 | * 427 | * Otherwise stops the test case and reports on standard output. 428 | * 429 | * Example: 430 | * ``` 431 | * atto_memeq("abcd", "abcd", 2); // Passes 432 | * atto_memeq("abcd", "abcd", 4); // Passes 433 | * atto_memeq("abcd", "abcd", 100); // UNDEFINED as exceeding known memory 434 | * atto_memeq("abcd", "ABCD", 4); // Fails 435 | * ``` 436 | */ 437 | #define atto_memeq(a, b, len) atto_assert(memcmp((a), (b), len) == 0) 438 | 439 | /** 440 | * Verifies if two memory sections are different within the given length. 441 | * 442 | * Otherwise stops the test case and reports on standard output. 443 | * 444 | * Example: 445 | * ``` 446 | * atto_memneq("abcd", "abcd", 2); // Fails 447 | * atto_memneq("abcd", "abcd", 4); // Fails 448 | * atto_memneq("abcd", "abcd", 100); // UNDEFINED as exceeding known memory 449 | * atto_memneq("abcd", "abCD", 4); // Passes 450 | * ``` 451 | */ 452 | #define atto_memneq(a, b, len) atto_assert(memcmp((a), (b), len) != 0) 453 | 454 | /** 455 | * Verifies if a memory section is filled with just zeros. 456 | * 457 | * Otherwise stops the test case and reports on standard output. 458 | * 459 | * Example: 460 | * ``` 461 | * atto_zeros("abcd", 2); // Fails 462 | * atto_zeros("\0\0cd", 2); // Passes 463 | * atto_zeros("\0\0cd", 4); // Fails 464 | * atto_zeros("\0\0\0\0", 4); // Passes 465 | * atto_zeros("\0\0\0\0", 100); // UNDEFINED as exceeding known memory 466 | * ``` 467 | */ 468 | #define atto_zeros(x, len) \ 469 | for (unsigned long long atto_idx = 0; atto_idx < (len); atto_idx ++) { \ 470 | atto_eq(((char*)x)[atto_idx], 0); \ 471 | } 472 | 473 | /** 474 | * Forces a failure of the test case, stopping it and reporting on standard 475 | * output. 476 | */ 477 | #define atto_fail() atto_assert(0) 478 | 479 | #ifdef __cplusplus 480 | } 481 | #endif 482 | 483 | #endif /* ATTO_H */ 484 | -------------------------------------------------------------------------------- /tst/test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * Unit test suite for VCAN. 5 | * 6 | * @copyright Copyright © 2020, Matjaž Guštin 7 | * . All rights reserved. 8 | * @license BSD 3-clause license. 9 | */ 10 | 11 | #include "atto.h" 12 | #include "vcan.h" 13 | #include 14 | #include 15 | 16 | static void test_init_null(void) 17 | { 18 | vcan_err_t err = vcan_init(NULL); 19 | 20 | atto_eq(err, VCAN_NULL_BUS); 21 | } 22 | 23 | static void test_init_valid(void) 24 | { 25 | vcan_bus_t bus; 26 | 27 | vcan_err_t err = vcan_init(&bus); 28 | 29 | atto_eq(err, VCAN_OK); 30 | atto_zeros((uint8_t*) &bus, sizeof(bus)) 31 | } 32 | 33 | static void test_connect_null_bus(void) 34 | { 35 | vcan_err_t err = vcan_connect(NULL, NULL); 36 | 37 | atto_eq(err, VCAN_NULL_BUS); 38 | } 39 | 40 | static void test_connect_null_node(void) 41 | { 42 | vcan_bus_t bus; 43 | vcan_err_t err = vcan_init(&bus); 44 | atto_eq(err, VCAN_OK); 45 | 46 | err = vcan_connect(&bus, NULL); 47 | 48 | atto_eq(err, VCAN_NULL_NODE); 49 | } 50 | 51 | static void test_connect_null_callback(void) 52 | { 53 | vcan_bus_t bus; 54 | vcan_err_t err = vcan_init(&bus); 55 | atto_eq(err, VCAN_OK); 56 | vcan_node_t node = { 57 | .callback_on_rx = NULL, 58 | .other_custom_data = NULL, 59 | }; 60 | 61 | err = vcan_connect(&bus, &node); 62 | 63 | atto_eq(err, VCAN_NULL_CALLBACK); 64 | } 65 | 66 | static void does_nothing(vcan_node_t* node, const vcan_msg_t* msg) 67 | { 68 | (void) node; 69 | (void) msg; 70 | } 71 | 72 | static void test_connect_valid(void) 73 | { 74 | vcan_bus_t bus; 75 | vcan_err_t err = vcan_init(&bus); 76 | atto_eq(err, VCAN_OK); 77 | vcan_node_t node = { 78 | .callback_on_rx = does_nothing, 79 | .other_custom_data = NULL, 80 | }; 81 | 82 | err = vcan_connect(&bus, &node); 83 | 84 | atto_eq(err, VCAN_OK); 85 | atto_eq(bus.connected, 1); 86 | atto_eq(bus.nodes[0], &node); 87 | } 88 | 89 | 90 | static void test_connect_already_connected(void) 91 | { 92 | vcan_bus_t bus; 93 | vcan_err_t err = vcan_init(&bus); 94 | atto_eq(err, VCAN_OK); 95 | vcan_node_t node = { 96 | .callback_on_rx = does_nothing, 97 | .other_custom_data = NULL, 98 | }; 99 | 100 | err = vcan_connect(&bus, &node); 101 | atto_eq(err, VCAN_OK); 102 | err = vcan_connect(&bus, &node); 103 | 104 | atto_eq(err, VCAN_ALREADY_CONNECTED); 105 | atto_eq(bus.connected, 1); 106 | atto_eq(bus.nodes[0], &node); 107 | } 108 | 109 | static void test_connect_max_reached(void) 110 | { 111 | vcan_bus_t bus; 112 | vcan_err_t err = vcan_init(&bus); 113 | atto_eq(err, VCAN_OK); 114 | vcan_node_t node = { 115 | .callback_on_rx = does_nothing, 116 | .other_custom_data = NULL, 117 | }; 118 | 119 | bus.connected = VCAN_MAX_CONNECTED_NODES; 120 | err = vcan_connect(&bus, &node); 121 | 122 | atto_eq(err, VCAN_TOO_MANY_CONNECTED); 123 | atto_eq(bus.connected, VCAN_MAX_CONNECTED_NODES); 124 | } 125 | 126 | 127 | static void test_disconnect_null_bus(void) 128 | { 129 | vcan_err_t err = vcan_disconnect(NULL, NULL); 130 | 131 | atto_eq(err, VCAN_NULL_BUS); 132 | } 133 | 134 | static void test_disconnect_null_node(void) 135 | { 136 | vcan_bus_t bus; 137 | vcan_err_t err = vcan_init(&bus); 138 | atto_eq(err, VCAN_OK); 139 | 140 | err = vcan_disconnect(&bus, NULL); 141 | 142 | atto_eq(err, VCAN_NULL_NODE); 143 | } 144 | 145 | static void test_disconnect_valid(void) 146 | { 147 | vcan_bus_t bus; 148 | vcan_err_t err = vcan_init(&bus); 149 | atto_eq(err, VCAN_OK); 150 | vcan_node_t node = { 151 | .callback_on_rx = does_nothing, 152 | .other_custom_data = NULL, 153 | }; 154 | err = vcan_connect(&bus, &node); 155 | atto_eq(err, VCAN_OK); 156 | 157 | err = vcan_disconnect(&bus, &node); 158 | 159 | atto_eq(err, VCAN_OK); 160 | atto_eq(bus.connected, 0); 161 | } 162 | 163 | static void test_disconnect_empty_bus(void) 164 | { 165 | vcan_bus_t bus; 166 | vcan_err_t err = vcan_init(&bus); 167 | atto_eq(err, VCAN_OK); 168 | vcan_node_t node = { 169 | .callback_on_rx = does_nothing, 170 | .other_custom_data = NULL, 171 | }; 172 | 173 | err = vcan_disconnect(&bus, &node); 174 | 175 | atto_eq(err, VCAN_NODE_NOT_FOUND); 176 | atto_eq(bus.connected, 0); 177 | } 178 | 179 | static void test_disconnect_not_found(void) 180 | { 181 | vcan_bus_t bus; 182 | vcan_err_t err = vcan_init(&bus); 183 | atto_eq(err, VCAN_OK); 184 | vcan_node_t node_1 = { 185 | .callback_on_rx = does_nothing, 186 | .other_custom_data = NULL, 187 | .id = 1 188 | }; 189 | vcan_node_t node_2 = { 190 | .callback_on_rx = does_nothing, 191 | .other_custom_data = NULL, 192 | .id = 2 193 | }; 194 | err = vcan_connect(&bus, &node_1); 195 | atto_eq(err, VCAN_OK); 196 | 197 | err = vcan_disconnect(&bus, &node_2); 198 | 199 | atto_eq(err, VCAN_NODE_NOT_FOUND); 200 | atto_eq(bus.connected, 1); 201 | atto_eq(bus.nodes[0], &node_1); 202 | } 203 | 204 | static void test_tx_null_bus(void) 205 | { 206 | vcan_err_t err = vcan_tx(NULL, NULL, NULL); 207 | 208 | atto_eq(err, VCAN_NULL_BUS); 209 | } 210 | 211 | static void test_tx_null_msg(void) 212 | { 213 | vcan_bus_t bus; 214 | vcan_err_t err = vcan_init(&bus); 215 | atto_eq(err, VCAN_OK); 216 | 217 | err = vcan_tx(&bus, NULL, NULL); 218 | 219 | atto_eq(err, VCAN_NULL_MSG); 220 | } 221 | 222 | 223 | static void test_tx_no_nodes_connected(void) 224 | { 225 | vcan_bus_t bus; 226 | vcan_err_t err = vcan_init(&bus); 227 | atto_eq(err, VCAN_OK); 228 | vcan_msg_t msg = { 229 | .id = 20, 230 | .len = 3, 231 | .data = {1, 2, 3} 232 | }; 233 | 234 | err = vcan_tx(&bus, &msg, NULL); 235 | 236 | atto_eq(err, VCAN_OK); 237 | atto_memeq(&msg, &bus.received_msg, sizeof(vcan_msg_t)); 238 | } 239 | 240 | static void sets_custom_data_to_1(vcan_node_t* node, const vcan_msg_t* msg) 241 | { 242 | (void) msg; 243 | node->other_custom_data = (void*) 1; 244 | } 245 | 246 | static void sets_custom_data_to_2(vcan_node_t* node, const vcan_msg_t* msg) 247 | { 248 | (void) msg; 249 | node->other_custom_data = (void*) 2; 250 | } 251 | 252 | static void test_tx_to_all_nodes(void) 253 | { 254 | vcan_bus_t bus; 255 | vcan_err_t err = vcan_init(&bus); 256 | atto_eq(err, VCAN_OK); 257 | vcan_node_t node_1 = { 258 | .callback_on_rx = sets_custom_data_to_1, 259 | .other_custom_data = NULL, 260 | .id = 1, 261 | }; 262 | vcan_node_t node_2 = { 263 | .callback_on_rx = sets_custom_data_to_1, 264 | .other_custom_data = NULL, 265 | .id = 2, 266 | }; 267 | err = vcan_connect(&bus, &node_1); 268 | atto_eq(err, VCAN_OK); 269 | err = vcan_connect(&bus, &node_2); 270 | atto_eq(err, VCAN_OK); 271 | // Setting one callback after connecting. 272 | node_2.callback_on_rx = sets_custom_data_to_2; 273 | vcan_msg_t msg = { 274 | .id = 20, 275 | .len = 3, 276 | .data = {1, 2, 3} 277 | }; 278 | 279 | err = vcan_tx(&bus, &msg, NULL); 280 | 281 | atto_eq(err, VCAN_OK); 282 | // Message was copied 283 | atto_memeq(&msg, &bus.received_msg, sizeof(vcan_msg_t)); 284 | // Callbacks were called 285 | atto_eq((int) node_1.other_custom_data, 1); 286 | atto_eq((int) node_2.other_custom_data, 2); 287 | } 288 | 289 | 290 | static void test_tx_to_all_nodes_except_source(void) 291 | { 292 | vcan_bus_t bus; 293 | vcan_err_t err = vcan_init(&bus); 294 | atto_eq(err, VCAN_OK); 295 | vcan_node_t node_1 = { 296 | .callback_on_rx = sets_custom_data_to_1, 297 | .other_custom_data = NULL, 298 | .id = 1 299 | }; 300 | vcan_node_t node_2 = { 301 | .callback_on_rx = does_nothing, 302 | .other_custom_data = NULL, 303 | .id = 2 304 | }; 305 | err = vcan_connect(&bus, &node_1); 306 | atto_eq(err, VCAN_OK); 307 | err = vcan_connect(&bus, &node_2); 308 | atto_eq(err, VCAN_OK); 309 | // Setting one callback after connecting. 310 | node_2.callback_on_rx = sets_custom_data_to_2; 311 | vcan_msg_t msg = { 312 | .id = 20, 313 | .len = 3, 314 | .data = {1, 2, 3} 315 | }; 316 | 317 | err = vcan_tx(&bus, &msg, &node_2); 318 | 319 | atto_eq(err, VCAN_OK); 320 | // Message was copied 321 | atto_memeq(&msg, &bus.received_msg, sizeof(vcan_msg_t)); 322 | // Callback was called 323 | atto_eq((int) node_1.other_custom_data, 1); 324 | // Source node untouched 325 | atto_eq(node_2.other_custom_data, NULL); 326 | } 327 | 328 | static void callback_print_msg(vcan_node_t* node, const vcan_msg_t* msg) 329 | { 330 | printf("Node %"PRIu32" received " 331 | "ID: 0x%08"PRIX32"" 332 | " | Len: %"PRIu32"" 333 | " | Data: ", 334 | node->id, 335 | msg->id, 336 | msg->len); 337 | for (size_t i = 0; i < msg->len; i++) 338 | { 339 | printf("%02"PRIX8" ", msg->data[i]); 340 | } 341 | puts(""); 342 | } 343 | 344 | static void test_readme_example(void) 345 | { 346 | // Create and initialise a virtual bus. 347 | vcan_bus_t bus; 348 | vcan_err_t err; 349 | err = vcan_init(&bus); // Fails on NULL args 350 | assert(err == VCAN_OK); 351 | 352 | // Create 3 virtual nodes. 353 | // Make them print the received message on reception. 354 | vcan_node_t node_1 = { 355 | .id = 1, 356 | .callback_on_rx = callback_print_msg, 357 | }; 358 | vcan_node_t node_2 = { 359 | .id = 2, 360 | .callback_on_rx = callback_print_msg, 361 | }; 362 | vcan_node_t node_3 = { 363 | .id = 3, 364 | .callback_on_rx = callback_print_msg, 365 | }; 366 | 367 | // Connect the nodes to the bus - by default up to 16 nodes. 368 | // It's just a #define constant, it can be changed easily. 369 | err = vcan_connect(&bus, &node_1); // Fails on NULL args or full bus 370 | assert(err == VCAN_OK); 371 | err = vcan_connect(&bus, &node_2); // Fails on NULL args or full bus 372 | assert(err == VCAN_OK); 373 | err = vcan_connect(&bus, &node_3); // Fails on NULL args or full bus 374 | assert(err == VCAN_OK); 375 | 376 | // Transmit! 377 | puts("Transmitting from node 1, node 2 and 3 receive."); 378 | const vcan_msg_t msg = { 379 | .id = 0xABCD, 380 | .len = 3, 381 | .data = {0x00, 0x1A, 0x2B} 382 | }; 383 | err = vcan_tx(&bus, &msg, &node_1); // Fails on NULL bus or NULL msg 384 | assert(err == VCAN_OK); 385 | 386 | // Because we transmitted from node 1, only node 2 and node 3 received the 387 | // message. Of course node 1 did not receive it, because it sent it. 388 | // The callbacks were triggered immediately and your stdout should 389 | // look like this: 390 | // 391 | // Transmitting from node 1, node 2 and 3 receive. 392 | // Node 2 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 393 | // Node 3 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 394 | 395 | // If you transmit from a NULL node, then all nodes receive the message. 396 | puts("Transmitting from NULL node, everyone receives."); 397 | err = vcan_tx(&bus, &msg, NULL); 398 | assert(err == VCAN_OK); 399 | 400 | // And now the stdout should look like this: 401 | // 402 | // Transmitting from NULL node, everyone receives. 403 | // Node 1 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 404 | // Node 2 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 405 | // Node 3 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 406 | 407 | // Disconnecting nodes is easy. 408 | err = vcan_disconnect(&bus, &node_2); // Fails on NULL args or already 409 | // disconnected node 410 | assert(err == VCAN_OK); 411 | 412 | // Now node_2 will not receive anything anymore. 413 | puts("Transmitting from node 1 after node 2 disconnection," 414 | " node 3 receives."); 415 | err = vcan_tx(&bus, &msg, &node_1); 416 | assert(err == VCAN_OK); 417 | 418 | // And now the stdout should look like this: 419 | // 420 | // Transmitting from node 1 after node 2 disconnection, node 3 receives. 421 | // Node 3 received ID: 0x0000ABCD | Len: 3 | Data: 00 1A 2B 422 | } 423 | 424 | int main(void) 425 | { 426 | test_init_null(); 427 | test_init_valid(); 428 | test_connect_null_bus(); 429 | test_connect_null_node(); 430 | test_connect_null_callback(); 431 | test_connect_valid(); 432 | test_connect_max_reached(); 433 | test_connect_already_connected(); 434 | test_disconnect_null_bus(); 435 | test_disconnect_null_node(); 436 | test_disconnect_empty_bus(); 437 | test_disconnect_not_found(); 438 | test_disconnect_valid(); 439 | test_tx_null_bus(); 440 | test_tx_null_msg(); 441 | test_tx_no_nodes_connected(); 442 | test_tx_to_all_nodes(); 443 | test_tx_to_all_nodes_except_source(); 444 | test_readme_example(); 445 | return atto_at_least_one_fail; 446 | } 447 | --------------------------------------------------------------------------------