From 93e9bcdf730fddcf098a6dfd449ce42badf0a8f1 Mon Sep 17 00:00:00 2001 From: "David J. Fiddes" <35607151+davefiddes@users.noreply.github.com> Date: Thu, 3 Oct 2024 18:06:33 +0100 Subject: [PATCH] Fix unsigned CAN receive mapping for 32-bit values and associated unit testing (#22) * Import unit tests from stm32-sine Pull in stm32-sine unit tests from commit 1b43e4c. Remove stm32-sine specific tests. Add minimal param_prj.h and hwdefs.h and missing libopencm3 stubs to get the unit tests to link and run. Construct a GitHub action to build just the unit tests on Linux * Create unsigned CAN receive tests This adds the missing unit tests to cover the receive mapping of CAN values where the value is larger than the limit of a signed value. This demonstrates that the unsigned mapping is working as expected. The CanMap code has a conditional define to allow building of libopeninv with signed receive support. Extend the unit test Makefile to support building with this and retaining the existing signed receive mapping tests. Extend the GitHub Action to build both variants. Tests: - Build with CAN_SIGNED=0 and verify that 32-bit receive tests are broken - Build with CAN_SIGNED=1 and verify existing tests pass * Fix unsigned CAN receive for 32-bit values With a receive CAN mapping for a 32-bit value if the value being received is > 2^31 then it is converted into a negative number rather than the large positive number that would be expected. The fix is to avoid storing the incoming value in a signed type when building the normal unsigned version of libopeninv. Tests: - Build and run unit tests with CAN_SIGNED=1 and CAN_SIGNED=0 with no errors --- .github/workflows/CI-build.yml | 37 ++ .gitignore | 2 + README.md | 3 + src/canmap.cpp | 29 +- test/Makefile | 34 + test/stub_canhardware.cpp | 45 ++ test/stub_canhardware.h | 48 ++ test/stub_libopencm3.c | 58 ++ test/test-include/hwdefs.h | 28 + test/test-include/param_prj.h | 27 + test/test.h | 36 + test/test_canmap.cpp | 1140 ++++++++++++++++++++++++++++++++ test/test_fp.cpp | 68 ++ test/test_fu.cpp | 75 +++ test/test_main.cpp | 61 ++ 15 files changed, 1677 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/CI-build.yml create mode 100644 .gitignore create mode 100644 test/Makefile create mode 100644 test/stub_canhardware.cpp create mode 100644 test/stub_canhardware.h create mode 100644 test/stub_libopencm3.c create mode 100644 test/test-include/hwdefs.h create mode 100644 test/test-include/param_prj.h create mode 100644 test/test.h create mode 100644 test/test_canmap.cpp create mode 100644 test/test_fp.cpp create mode 100644 test/test_fu.cpp create mode 100644 test/test_main.cpp diff --git a/.github/workflows/CI-build.yml b/.github/workflows/CI-build.yml new file mode 100644 index 0000000..a88245e --- /dev/null +++ b/.github/workflows/CI-build.yml @@ -0,0 +1,37 @@ +name: CI +on: + push: + pull_request: + +jobs: + build: + name: build-linux + runs-on: ubuntu-latest + + steps: + - name: Checkout libopeninv + uses: actions/checkout@v4 + with: + path: libopeninv + + - name: Checkout libopencm3 + uses: actions/checkout@v4 + with: + repository: jsphuebner/libopencm3 + path: libopencm3 + + - name: Build unit tests on host + run: | + make -C libopeninv/test clean all CAN_SIGNED=0 + + - name: Run unit tests on host + run: | + libopeninv/test/test_libopeninv + + - name: Build unit tests on host (Signed CAN receive) + run: | + make -C libopeninv/test clean all CAN_SIGNED=1 + + - name: Run unit tests on host (Signed CAN receive) + run: | + libopeninv/test/test_libopeninv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..acc82ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +test/test_libopeninv diff --git a/README.md b/README.md index 48ba7b9..c4da183 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # libopeninv + +[![Build status](../../actions/workflows/CI-build.yml/badge.svg)](../../actions/workflows/CI-build.yml) + Generic modules that can be used in many projects diff --git a/src/canmap.cpp b/src/canmap.cpp index 81dfd71..52f3d45 100644 --- a/src/canmap.cpp +++ b/src/canmap.cpp @@ -86,7 +86,6 @@ void CanMap::HandleRx(uint32_t canId, uint32_t data[2], uint8_t) { forEachPosMap(curPos, recvMap) { - float val; uint32_t word; uint8_t pos = curPos->offsetBits; uint8_t numBits = ABS(curPos->numBits); @@ -138,22 +137,24 @@ void CanMap::HandleRx(uint32_t canId, uint32_t data[2], uint8_t) uint32_t mask = (1L << numBits) - 1; word = (word >> pos) & mask; - // sign-extend our arbitrary sized integer out to 32-bits but only if - // it is bigger than a single bit - int32_t ival; #if CAN_SIGNED - if (numBits > 1) - { - uint32_t sign_bit = 1L << (numBits - 1); - ival = static_cast(((word + sign_bit) & mask)) - sign_bit; - } - else + // sign-extend our arbitrary sized integer out to 32-bits but only if + // it is bigger than a single bit + int32_t ival; + if (numBits > 1) + { + uint32_t sign_bit = 1L << (numBits - 1); + ival = static_cast(((word + sign_bit) & mask)) - sign_bit; + } + else + { + ival = word; + } + float val = ival; + #else + float val = word; #endif - { - ival = word; - } - val = ival; val += curPos->offset; val *= curPos->gain; diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..d8e0a71 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,34 @@ +# Option to allow signed reception of CAN variables +CAN_SIGNED ?= 0 + +CC = gcc +CPP = g++ +LD = g++ +CFLAGS = -std=c99 -ggdb -DSTM32F1 -DCAN_SIGNED=$(CAN_SIGNED) -Itest-include -I../include -I../../libopencm3/include +CPPFLAGS = -ggdb -DSTM32F1 -DCAN_SIGNED=$(CAN_SIGNED) -Itest-include -I../include -I../../libopencm3/include +LDFLAGS = -g +BINARY = test_libopeninv +OBJS = test_main.o fu.o test_fu.o test_fp.o my_fp.o my_string.o params.o \ + stub_canhardware.o test_canmap.o canmap.o \ + stub_libopencm3.o +VPATH = ../src ../libopeninv/src + +# Check if the variable GITHUB_RUN_NUMBER exists. When running on the github actions running, this +# variable is automatically available. +# Create a compiler define with the content of the variable. Or, if it does not exist, use replacement value 99999. +CPPFLAGS += $(shell \ + if [ -z "$$GITHUB_RUN_NUMBER" ]; then echo "-DGITHUB_RUN_NUMBER=0"; else echo "-DGITHUB_RUN_NUMBER=$$GITHUB_RUN_NUMBER"; fi ) + +all: $(BINARY) + +$(BINARY): $(OBJS) + $(LD) $(LDFLAGS) -o $(BINARY) $(OBJS) + +%.o: ../%.cpp + $(CPP) $(CPPFLAGS) -o $@ -c $< + +%.o: ../%.c + $(CC) $(CFLAGS) -o $@ -c $< + +clean: + rm -f $(OBJS) $(BINARY) diff --git a/test/stub_canhardware.cpp b/test/stub_canhardware.cpp new file mode 100644 index 0000000..c82b5a8 --- /dev/null +++ b/test/stub_canhardware.cpp @@ -0,0 +1,45 @@ +/* + * This file is part of the stm32-sine project. + * + * Copyright (C) 2021 Johannes Huebner + * Copyright (C) 2024 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "stub_canhardware.h" + +CanCallback* vcuCan = nullptr; +uint32_t vcuCanId; + +CanHardware::CanHardware() +{} + +bool CanHardware::AddCallback(CanCallback* cb) +{ + vcuCan = cb; + return true; +} + +bool CanHardware::RegisterUserMessage(uint32_t canId, uint32_t mask) +{ + vcuCanId = canId; + return true; +} + +void CanHardware::ClearUserMessages() {} + +void CanHardware::HandleRx(uint32_t canId, uint32_t data[2], uint8_t dlc) +{ + vcuCan->HandleRx(canId, data, dlc); +} diff --git a/test/stub_canhardware.h b/test/stub_canhardware.h new file mode 100644 index 0000000..13331cb --- /dev/null +++ b/test/stub_canhardware.h @@ -0,0 +1,48 @@ +/* + * This file is part of the stm32-sine project. + * + * Copyright (C) 2021 Johannes Huebner + * Copyright (C) 2024 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef TEST_CANHARDWARE_H +#define TEST_CANHARDWARE_H + +#include "canhardware.h" +#include +#include +#include + +class CanStub: public CanHardware +{ + void SetBaudrate(enum baudrates baudrate) {} + void Send(uint32_t canId, uint32_t data[2], uint8_t len) + { + m_canId = canId; + memcpy(&m_data[0], &data[0], sizeof(m_data)); + m_len = len; + } + virtual void ConfigureFilters() {} + +public: + std::array m_data; + uint8_t m_len; + uint32_t m_canId; +}; + +extern CanCallback* vcuCan; +extern uint32_t vcuCanId; + +#endif // TEST_CANHARDWARE_H \ No newline at end of file diff --git a/test/stub_libopencm3.c b/test/stub_libopencm3.c new file mode 100644 index 0000000..29dd208 --- /dev/null +++ b/test/stub_libopencm3.c @@ -0,0 +1,58 @@ +/* + * This file is part of the stm32-sine project. + * + * Copyright (C) 2024 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "stdint.h" + +void flash_unlock(void) +{ +} + +void flash_lock(void) +{ +} + +void flash_set_ws(uint32_t ws) +{ +} + +void flash_program_word(uint32_t address, uint32_t data) +{ +} + +void flash_erase_page(uint32_t page_address) +{ +} + +uint16_t desig_get_flash_size(void) +{ + return 8; +} + +uint32_t crc_calculate(uint32_t data) +{ + return 0xaa55; +} + +uint32_t crc_calculate_block(uint32_t *datap, int size) +{ + return 0xaaaa5555; +} + +void crc_reset(void) +{ +} diff --git a/test/test-include/hwdefs.h b/test/test-include/hwdefs.h new file mode 100644 index 0000000..151a150 --- /dev/null +++ b/test/test-include/hwdefs.h @@ -0,0 +1,28 @@ +/* + * This file is part of the libopeninv project. + * + * Copyright (C) 2024 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef HWDEFS_H +#define HWDEFS_H + +// Minimal project hardware defines to test libopeninv + +#define FLASH_PAGE_SIZE 1024 + +#define CAN1_BLKNUM 2 // second to last block of 1k + +#endif \ No newline at end of file diff --git a/test/test-include/param_prj.h b/test/test-include/param_prj.h new file mode 100644 index 0000000..2713cc8 --- /dev/null +++ b/test/test-include/param_prj.h @@ -0,0 +1,27 @@ +/* + * This file is part of the libopeninv project. + * + * Copyright (C) 2024 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Minimal project parameters to test libopeninv +/* category name unit min max default id */ +#define PARAM_LIST \ + VALUE_ENTRY(amp, "dig", 2013 ) \ + VALUE_ENTRY(pot, "dig", 2015 ) \ + PARAM_ENTRY("inverter", ocurlim, "A", -65536, 65536, 100, 22 ) + +extern const char* errorListString; \ No newline at end of file diff --git a/test/test.h b/test/test.h new file mode 100644 index 0000000..0c88731 --- /dev/null +++ b/test/test.h @@ -0,0 +1,36 @@ +#ifndef TEST_H_INCLUDED +#define TEST_H_INCLUDED +#include +#include + +typedef void (*VoidFunction)(); + +class UnitTest +{ + public: + UnitTest(const std::list*); + virtual void TestSetup() {} + virtual void TestCaseSetup() {} + const std::list GetCases() { return *_cases; } + void SetTestCaseList(const std::list* cases) { _cases = cases; } + + private: + const std::list* _cases; +}; + +extern int _failedAssertions; + + +#define REGISTER_TEST(t, ...) static UnitTest* test = new t (new std::list { __VA_ARGS__ }); + +#define STRING(s) #s +#define ASSERT(c) \ + if (c) \ + std::cout << "Test " << __FILE__ << "::" << __func__ << " passed." << std::endl; \ + else \ + { \ + std::cout << "Assertion failed: " << STRING(c) << " in " __FILE__ " : " << std::dec << __LINE__ << std::endl; \ + _failedAssertions++; \ + } + +#endif // TEST_H_INCLUDED diff --git a/test/test_canmap.cpp b/test/test_canmap.cpp new file mode 100644 index 0000000..00b4b6a --- /dev/null +++ b/test/test_canmap.cpp @@ -0,0 +1,1140 @@ +/* + * This file is part of the stm32-sine project. + * + * Copyright (C) 2024 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "canhardware.h" +#include "canmap.h" +#include "params.h" +#include "stub_canhardware.h" +#include "test.h" + +#include +#include +#include +#include +#include +#include +#include + +class CanMapTest : public UnitTest +{ +public: + explicit CanMapTest(const std::list* cases) : UnitTest(cases) + { + } + virtual void TestCaseSetup(); +}; + +std::unique_ptr canStub; +std::unique_ptr canMap; + +void Param::Change(Param::PARAM_NUM paramNum) +{ + // Dummy stub +} + +void CanMapTest::TestCaseSetup() +{ + canStub = std::make_unique(); + canMap = std::make_unique(canStub.get(), false); + Param::LoadDefaults(); +} + +const uint32_t CanId = 0x123; + +std::ostream& operator<<(std::ostream& o, const std::array& data) +{ + for (const auto& element : data) + { + o << "0x" << std::setfill('0') << std::setw(2) << std::hex + << (int)element << " "; + } + return o; +} + +bool FrameMatches(const std::array& expected) +{ + if (canStub->m_canId != CanId) + { + std::cout << "CAN ID doesn't match. Expected: " << CanId + << " Actual: " << canStub->m_canId << "\n"; + return false; + } + + if (canStub->m_len != 8) + { + std::cout << "CAN frame length doesn't match. Expected: 8 " + << "Actual: " << canStub->m_len << "\n"; + return false; + } + + if (canStub->m_data != expected) + { + std::cout << "CAN frame data doesn't match.\n" + << "Actual : " << canStub->m_data << "\n" + << "Expected: " << expected << "\n"; + + return false; + } + + return true; +} + +static void SendFrame(const std::array& frame) +{ + canStub->HandleRx(CanId, (uint32_t*)&frame[0], 8); +} + +static void send_map_little_endian_byte_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 0, 8, 1.0, 0); + Param::SetFloat(Param::ocurlim, 0x42); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0x42, 0, 0, 0, 0, 0, 0, 0 })); +} + +static void send_map_little_endian_16_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 0, 16, 1.0, 0); + Param::SetFloat(Param::ocurlim, 0x42); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0x42, 0, 0, 0, 0, 0, 0, 0 })); +} + +static void send_map_little_endian_32_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 0, 32, 1.0, 0); + Param::SetFloat(Param::ocurlim, 0x42); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0x42, 0, 0, 0, 0, 0, 0, 0 })); +} + +static void send_map_little_endian_32_bit_in_second_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 32, 32, 1.0, 0); + Param::SetFloat(Param::ocurlim, 0x42); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0, 0x42, 0, 0, 0 })); +} + +static void send_map_little_endian_negative_number_16_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 0, 16, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0xfe, 0xff, 0, 0, 0, 0, 0, 0 })); +} + +static void send_map_little_endian_negative_number_24_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 0, 24, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0xfe, 0xff, 0xff, 0, 0, 0, 0, 0 })); +} + +static void send_map_little_endian_negative_number_32_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 0, 32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0xfe, 0xff, 0xff, 0xff, 0, 0, 0, 0 })); +} + +static void send_map_little_endian_negative_number_32_bit_in_second_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 32, 32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0, 0xfe, 0xff, 0xff, 0xff })); +} + +static void send_map_little_endian_negative_number_32_bit_spanning_both_words() +{ + canMap->AddSend(Param::ocurlim, CanId, 16, 32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0xfe, 0xff, 0xff, 0xff, 0, 0 })); +} + +static void send_map_little_endian_negative_number_32_bit_mostly_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 8, 32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0xfe, 0xff, 0xff, 0xff, 0, 0, 0 })); +} + +static void +send_map_little_endian_negative_number_32_bit_mostly_in_second_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 24, 32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0xfe, 0xff, 0xff, 0xff, 0 })); +} + +static void send_map_little_endian_negative_number_16_bit_at_end_of_frame() +{ + canMap->AddSend(Param::ocurlim, CanId, 48, 16, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0, 0, 0, 0xfe, 0xff })); +} + +static void send_map_big_endian_byte_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 7, -8, 1.0, 0); + Param::SetFloat(Param::ocurlim, 0x42); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0x42, 0, 0, 0, 0, 0, 0, 0 })); +} + +static void send_map_big_endian_16_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 23, -16, 1.0, 0); + Param::SetFloat(Param::ocurlim, 0x42); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0x42, 0, 0, 0, 0, 0 })); +} + +static void send_map_big_endian_32_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 31, -32, 1.0, 0); + Param::SetFloat(Param::ocurlim, 0x42); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0x42, 0, 0, 0, 0 })); +} + +static void send_map_big_endian_negative_byte_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 7, -8, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0xfe, 0, 0, 0, 0, 0, 0, 0 })); +} + +static void send_map_big_endian_negative_16_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 23, -16, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0xff, 0xfe, 0, 0, 0, 0, 0 })); +} + +static void send_map_big_endian_negative_24_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 23, -24, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0xff, 0xff, 0xfe, 0, 0, 0, 0, 0 })); +} + +static void send_map_big_endian_negative_32_bit_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 31, -32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0xff, 0xff, 0xff, 0xfe, 0, 0, 0, 0 })); +} + +static void send_map_big_endian_negative_byte_in_second_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 39, -8, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0, 0xfe, 0, 0, 0 })); +} + +static void send_map_big_endian_negative_16_bit_in_second_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 47, -16, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0, 0xff, 0xfe, 0, 0 })); +} + +static void send_map_big_endian_negative_24_bit_in_second_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 55, -24, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0, 0xff, 0xff, 0xfe, 0 })); +} + +static void send_map_big_endian_negative_32_bit_in_second_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 63, -32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xfe })); +} + +static void send_map_big_endian_negative_number_24_bit_at_end_of_frame() +{ + canMap->AddSend(Param::ocurlim, CanId, 63, -24, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0, 0, 0xff, 0xff, 0xfe })); +} + +static void send_map_big_endian_negative_16_bit_spanning_both_words() +{ + canMap->AddSend(Param::ocurlim, CanId, 39, -16, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0xff, 0xfe, 0, 0, 0 })); +} + +static void send_map_big_endian_negative_32_bit_spanning_both_words() +{ + canMap->AddSend(Param::ocurlim, CanId, 47, -32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0xff, 0xff, 0xff, 0xfe, 0, 0 })); +} + +static void send_map_big_endian_negative_32_bit_mostly_in_first_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 39, -32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0xff, 0xff, 0xff, 0xfe, 0, 0, 0 })); +} + +static void send_map_big_endian_negative_32_bit_mostly_in_second_word() +{ + canMap->AddSend(Param::ocurlim, CanId, 55, -32, 1.0, 0); + Param::SetFloat(Param::ocurlim, -2); + + canMap->SendAll(); + + ASSERT(FrameMatches({ 0, 0, 0, 0xff, 0xff, 0xff, 0xfe, 0 })); +} + +static void receive_map_little_endian_byte_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 0, 8, 1.0, 0); + + SendFrame({ 42, 0, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == 42); +} + +static void receive_map_little_endian_16_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 0, 16, 1.0, 0); + + SendFrame({ 42, 0, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == 42); +} + +static void receive_map_little_endian_32_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 0, 32, 1.0, 0); + + SendFrame({ 42, 0, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == 42); +} + +static void receive_map_little_endian_32_bit_in_second_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 32, 32, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 42, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == 42); +} + +static void receive_map_big_endian_byte_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 7, -8, 1.0, 0); + + SendFrame({ 42, 0, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == 42); +} + +static void receive_map_big_endian_16_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 23, -16, 1.0, 0); + + SendFrame({ 0, 0, 42, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == 42); +} + +static void receive_map_big_endian_31_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 31, -31, 1.0, 0); + + SendFrame({ 0x80, 0, 0, 42, 0x80, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == 42); +} + +static void receive_map_big_endian_32_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 31, -32, 1.0, 0); + + SendFrame({ 0, 0, 0, 42, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == 42); +} + +static void receive_map_little_endian_single_bit_first_bit() +{ + canMap->AddRecv(Param::ocurlim, CanId, 0, 1, 1.0, 0); + + SendFrame({ 0x1, 0, 0, 0, 0, 0, 0, 0 }); + ASSERT(Param::GetBool(Param::ocurlim)); + + SendFrame({ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }); + ASSERT(!Param::GetBool(Param::ocurlim)); +} + +static void receive_map_big_endian_single_bit_first_bit() +{ + canMap->AddRecv(Param::ocurlim, CanId, 0, -1, 1.0, 0); + + SendFrame({ 0x80, 0, 0, 0, 0, 0, 0, 0 }); + ASSERT(Param::GetBool(Param::ocurlim)); + + SendFrame({ 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }); + ASSERT(!Param::GetBool(Param::ocurlim)); +} + +static void receive_map_little_endian_single_bit_last_bit() +{ + canMap->AddRecv(Param::ocurlim, CanId, 63, 1, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0, 0, 0, 0x80 }); + ASSERT(Param::GetBool(Param::ocurlim)); + + SendFrame({ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }); + ASSERT(!Param::GetBool(Param::ocurlim)); +} + +static void receive_map_big_endian_single_bit_last_bit() +{ + canMap->AddRecv(Param::ocurlim, CanId, 63, -1, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0, 0, 0, 1 }); + ASSERT(Param::GetBool(Param::ocurlim)); + + SendFrame({ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe }); + ASSERT(!Param::GetBool(Param::ocurlim)); +} + +static void receive_map_little_endian_single_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 21, 1, 1.0, 0); + + SendFrame({ 0, 0, 0x20, 0, 0, 0, 0, 0 }); + ASSERT(Param::GetBool(Param::ocurlim)); + + SendFrame({ 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff }); + ASSERT(!Param::GetBool(Param::ocurlim)); +} + +static void receive_map_little_endian_single_bit_in_second_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 53, 1, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0, 0, 0x20, 0 }); + ASSERT(Param::GetBool(Param::ocurlim)); + + SendFrame({ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff }); + ASSERT(!Param::GetBool(Param::ocurlim)); +} + +static void fail_to_map_with_invalid_can_id() +{ + ASSERT( + canMap->AddSend(Param::amp, 0x800, 0, 16, 1.0) == CAN_ERR_INVALID_ID); + + ASSERT( + canMap->AddSend(Param::amp, 0x40000000, 0, 16, 1.0) == + CAN_ERR_INVALID_ID); +} + +static void fail_to_map_with_invalid_little_endian_offset() +{ + ASSERT( + canMap->AddSend(Param::amp, 0x123, -1, 1, 1.0) == CAN_ERR_INVALID_OFS); + ASSERT( + canMap->AddSend(Param::amp, 0x123, 64, 1, 1.0) == CAN_ERR_INVALID_OFS); +} + +static void fail_to_map_with_invalid_little_endian_length() +{ + ASSERT( + canMap->AddSend(Param::amp, 0x123, 0, 0, 1.0) == CAN_ERR_INVALID_LEN); + ASSERT( + canMap->AddSend(Param::amp, 0x123, 0, 33, 1.0) == CAN_ERR_INVALID_LEN); +} + +static void fail_to_map_with_invalid_little_endian_total_struct_offset() +{ + ASSERT( + canMap->AddSend(Param::amp, 0x123, 63, 2, 1.0) == CAN_ERR_INVALID_OFS); + ASSERT( + canMap->AddSend(Param::amp, 0x123, 49, 16, 1.0) == CAN_ERR_INVALID_OFS); +} + +static void fail_to_map_with_invalid_big_endian_offset() +{ + ASSERT( + canMap->AddSend(Param::amp, 0x123, -1, -1, 1.0) == CAN_ERR_INVALID_OFS); + ASSERT( + canMap->AddSend(Param::amp, 0x123, 64, -1, 1.0) == CAN_ERR_INVALID_OFS); +} + +static void fail_to_map_with_invalid_big_endian_length() +{ + ASSERT( + canMap->AddSend(Param::amp, 0x123, 0, -33, 1.0) == CAN_ERR_INVALID_LEN); +} + +static void fail_to_map_with_invalid_big_endian_total_struct_offset() +{ + ASSERT( + canMap->AddSend(Param::amp, 0x123, 0, -2, 1.0) == CAN_ERR_INVALID_OFS); + ASSERT( + canMap->AddSend(Param::amp, 0x123, 14, -16, 1.0) == + CAN_ERR_INVALID_OFS); + ASSERT( + canMap->AddSend(Param::amp, 0x123, 7, -32, 1.0) == CAN_ERR_INVALID_OFS); + ASSERT( + canMap->AddSend(Param::amp, 0x123, 30, -32, 1.0) == + CAN_ERR_INVALID_OFS); +} + +#if CAN_SIGNED + +static void receive_map_little_endian_negative_number_16_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 0, 16, 1.0, 0); + + SendFrame({ 0xfe, 0xff, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_little_endian_negative_number_24_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 0, 24, 1.0, 0); + + SendFrame({ 0xfe, 0xff, 0xff, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_little_endian_negative_number_31_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 1, 31, 1.0, 0); + + SendFrame({ 0xfc, 0xff, 0xff, 0xff, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_little_endian_negative_number_32_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 0, 32, 1.0, 0); + + SendFrame({ 0xfe, 0xff, 0xff, 0xff, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_little_endian_negative_number_32_bit_in_second_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 32, 32, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0xfe, 0xff, 0xff, 0xff }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void +receive_map_little_endian_negative_number_32_bit_spanning_both_words() +{ + canMap->AddRecv(Param::ocurlim, CanId, 16, 32, 1.0, 0); + + SendFrame({ 0, 0, 0xfe, 0xff, 0xff, 0xff, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void +receive_map_little_endian_negative_number_32_bit_mostly_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 8, 32, 1.0, 0); + + SendFrame({ 0, 0xfe, 0xff, 0xff, 0xff, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void +receive_map_little_endian_negative_number_32_bit_mostly_in_second_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 24, 32, 1.0, 0); + + SendFrame({ 0, 0, 0, 0xfe, 0xff, 0xff, 0xff, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_little_endian_negative_number_16_bit_at_end_of_frame() +{ + canMap->AddRecv(Param::ocurlim, CanId, 48, 16, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0, 0, 0xfe, 0xff }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_byte_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 7, -8, 1.0, 0); + + SendFrame({ 0xfe, 0, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_16_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 23, -16, 1.0, 0); + + SendFrame({ 0, 0xff, 0xfe, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_24_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 23, -24, 1.0, 0); + + SendFrame({ 0xff, 0xff, 0xfe, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_31_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 31, -31, 1.0, 0); + + SendFrame({ 0x7f, 0xff, 0xff, 0xfe, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_32_bit_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 31, -32, 1.0, 0); + + SendFrame({ 0xff, 0xff, 0xff, 0xfe, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_byte_in_second_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 39, -8, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0xfe, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_16_bit_in_second_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 47, -16, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0xff, 0xfe, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_24_bit_in_second_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 55, -24, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0xff, 0xff, 0xfe, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_32_bit_in_second_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 63, -32, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xfe }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_number_24_bit_at_end_of_frame() +{ + canMap->AddRecv(Param::ocurlim, CanId, 63, -24, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0, 0xff, 0xff, 0xfe }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_16_bit_spanning_both_words() +{ + canMap->AddRecv(Param::ocurlim, CanId, 39, -16, 1.0, 0); + + SendFrame({ 0, 0, 0, 0xff, 0xfe, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_32_bit_spanning_both_words() +{ + canMap->AddRecv(Param::ocurlim, CanId, 47, -32, 1.0, 0); + + SendFrame({ 0, 0, 0xff, 0xff, 0xff, 0xfe, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_32_bit_mostly_in_first_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 39, -32, 1.0, 0); + + SendFrame({ 0, 0xff, 0xff, 0xff, 0xfe, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +static void receive_map_big_endian_negative_32_bit_mostly_in_second_word() +{ + canMap->AddRecv(Param::ocurlim, CanId, 55, -32, 1.0, 0); + + SendFrame({ 0, 0, 0, 0xff, 0xff, 0xff, 0xfe, 0 }); + + ASSERT(Param::GetInt(Param::ocurlim) == -2); +} + +#define RECEIVE_TESTS \ + receive_map_little_endian_negative_number_16_bit_in_first_word, \ + receive_map_little_endian_negative_number_24_bit_in_first_word, \ + receive_map_little_endian_negative_number_31_bit_in_first_word, \ + receive_map_little_endian_negative_number_32_bit_in_first_word, \ + receive_map_little_endian_negative_number_32_bit_in_second_word, \ + receive_map_little_endian_negative_number_32_bit_spanning_both_words, \ + receive_map_little_endian_negative_number_32_bit_mostly_in_first_word, \ + receive_map_little_endian_negative_number_32_bit_mostly_in_second_word, \ + receive_map_little_endian_negative_number_16_bit_at_end_of_frame, \ + receive_map_big_endian_negative_byte_in_first_word, \ + receive_map_big_endian_negative_16_bit_in_first_word, \ + receive_map_big_endian_negative_24_bit_in_first_word, \ + receive_map_big_endian_negative_31_bit_in_first_word, \ + receive_map_big_endian_negative_32_bit_in_first_word, \ + receive_map_big_endian_negative_byte_in_second_word, \ + receive_map_big_endian_negative_16_bit_in_second_word, \ + receive_map_big_endian_negative_24_bit_in_second_word, \ + receive_map_big_endian_negative_32_bit_in_second_word, \ + receive_map_big_endian_negative_number_24_bit_at_end_of_frame, \ + receive_map_big_endian_negative_16_bit_spanning_both_words, \ + receive_map_big_endian_negative_32_bit_spanning_both_words, \ + receive_map_big_endian_negative_32_bit_mostly_in_first_word, \ + receive_map_big_endian_negative_32_bit_mostly_in_second_word, + +#else // CAN_SIGNED + +static void receive_map_little_endian_12_bit_small_throttle_value() +{ + canMap->AddRecv(Param::pot, CanId, 0, 12, 1.0, 0); + + SendFrame({ 0x64, 0, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::pot) == 100); +} + +static void receive_map_little_endian_12_bit_large_throttle_value() +{ + canMap->AddRecv(Param::pot, CanId, 0, 12, 1.0, 0); + + SendFrame({ 0xAC, 0x0d, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::pot) == 3500); +} + +static void receive_map_little_endian_large_number_16_bit_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 0, 16, 1.0, 0); + + SendFrame({ 0xdc, 0xfe, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::amp) == 0xfedc); +} + +static void receive_map_little_endian_large_number_24_bit_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 0, 24, 1.0, 0); + + SendFrame({ 0xba, 0xdc, 0xfe, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::amp) == 0xfedcba); +} + +static void receive_map_little_endian_large_number_31_bit_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 1, 31, 1.0 / 256, 0); + + SendFrame({ 0xfe, 0xff, 0xff, 0xff, 0, 0, 0, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x800000); +} + +static void receive_map_little_endian_large_number_32_bit_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 0, 32, 1.0 / 256, 0); + + SendFrame({ 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +static void receive_map_little_endian_large_number_32_bit_in_second_word() +{ + canMap->AddRecv(Param::amp, CanId, 32, 32, 1.0 / 256, 0); + + SendFrame({ 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +static void receive_map_little_endian_large_number_32_bit_spanning_both_words() +{ + canMap->AddRecv(Param::amp, CanId, 16, 32, 1.0 / 256, 0); + + SendFrame({ 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +static void receive_map_little_endian_large_number_32_bit_mostly_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 8, 32, 1.0 / 256, 0); + + SendFrame({ 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +static void +receive_map_little_endian_large_number_32_bit_mostly_in_second_word() +{ + canMap->AddRecv(Param::amp, CanId, 24, 32, 1.0 / 256, 0); + + SendFrame({ 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +static void receive_map_little_endian_large_number_16_bit_at_end_of_frame() +{ + canMap->AddRecv(Param::amp, CanId, 48, 16, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0, 0, 0xdc, 0xfe }); + + ASSERT(Param::GetInt(Param::amp) == 65244); +} + +static void receive_map_big_endian_large_byte_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 7, -8, 1.0, 0); + + SendFrame({ 0xfe, 0, 0, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::amp) == 0xfe); +} + +static void receive_map_big_endian_large_16_bit_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 23, -16, 1.0, 0); + + SendFrame({ 0, 0xfe, 0xdc, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::amp) == 0xfedc); +} + +static void receive_map_big_endian_large_24_bit_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 23, -24, 1.0, 0); + + SendFrame({ 0xfe, 0xdc, 0xba, 0, 0, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::amp) == 0xfedcba); +} + +static void receive_map_big_endian_large_31_bit_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 31, -31, 1.0 / 256, 0); + + SendFrame({ 0x7f, 0xff, 0xff, 0xff, 0, 0, 0, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x800000); +} + +static void receive_map_big_endian_large_32_bit_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 31, -32, 1.0 / 256, 0); + + SendFrame({ 0xff, 0xff, 0xff, 0xfe, 0, 0, 0, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +static void receive_map_big_endian_large_byte_in_second_word() +{ + canMap->AddRecv(Param::amp, CanId, 39, -8, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0xfe, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::amp) == 0xfe); +} + +static void receive_map_big_endian_large_16_bit_in_second_word() +{ + canMap->AddRecv(Param::amp, CanId, 47, -16, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0xfe, 0xdc, 0, 0 }); + + ASSERT(Param::GetInt(Param::amp) == 0xfedc); +} + +static void receive_map_big_endian_large_24_bit_in_second_word() +{ + canMap->AddRecv(Param::amp, CanId, 55, -24, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0xfe, 0xdc, 0xba, 0 }); + + ASSERT(Param::GetInt(Param::amp) == 0xfedcba); +} + +static void receive_map_big_endian_large_32_bit_in_second_word() +{ + canMap->AddRecv(Param::amp, CanId, 63, -32, 1.0 / 256, 0); + + SendFrame({ 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +static void receive_map_big_endian_large_number_24_bit_at_end_of_frame() +{ + canMap->AddRecv(Param::amp, CanId, 63, -24, 1.0, 0); + + SendFrame({ 0, 0, 0, 0, 0, 0xfe, 0xdc, 0xba }); + + ASSERT(Param::GetInt(Param::amp) == 0xfedcba); +} + +static void receive_map_big_endian_large_16_bit_spanning_both_words() +{ + canMap->AddRecv(Param::amp, CanId, 39, -16, 1.0, 0); + + SendFrame({ 0, 0, 0, 0xfe, 0xdc, 0, 0, 0 }); + + ASSERT(Param::GetInt(Param::amp) == 0xfedc); +} + +static void receive_map_big_endian_large_32_bit_spanning_both_words() +{ + canMap->AddRecv(Param::amp, CanId, 47, -32, 1.0 / 256, 0); + + SendFrame({ 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +static void receive_map_big_endian_large_32_bit_mostly_in_first_word() +{ + canMap->AddRecv(Param::amp, CanId, 39, -32, 1.0 / 256, 0); + + SendFrame({ 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +static void receive_map_big_endian_large_32_bit_mostly_in_second_word() +{ + canMap->AddRecv(Param::amp, CanId, 55, -32, 1.0 / 256, 0); + + SendFrame({ 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0 }); + + // Slight rounding due to moving through a float + ASSERT(Param::GetInt(Param::amp) == 0x1000000); +} + +#define RECEIVE_TESTS \ + receive_map_little_endian_12_bit_small_throttle_value, \ + receive_map_little_endian_12_bit_large_throttle_value, \ + receive_map_little_endian_large_number_16_bit_in_first_word, \ + receive_map_little_endian_large_number_24_bit_in_first_word, \ + receive_map_little_endian_large_number_31_bit_in_first_word, \ + receive_map_little_endian_large_number_32_bit_in_first_word, \ + receive_map_little_endian_large_number_32_bit_in_second_word, \ + receive_map_little_endian_large_number_32_bit_spanning_both_words, \ + receive_map_little_endian_large_number_32_bit_mostly_in_first_word, \ + receive_map_little_endian_large_number_32_bit_mostly_in_second_word, \ + receive_map_little_endian_large_number_16_bit_at_end_of_frame, \ + receive_map_big_endian_large_byte_in_first_word, \ + receive_map_big_endian_large_16_bit_in_first_word, \ + receive_map_big_endian_large_24_bit_in_first_word, \ + receive_map_big_endian_large_31_bit_in_first_word, \ + receive_map_big_endian_large_32_bit_in_first_word, \ + receive_map_big_endian_large_byte_in_second_word, \ + receive_map_big_endian_large_16_bit_in_second_word, \ + receive_map_big_endian_large_24_bit_in_second_word, \ + receive_map_big_endian_large_32_bit_in_second_word, \ + receive_map_big_endian_large_number_24_bit_at_end_of_frame, \ + receive_map_big_endian_large_16_bit_spanning_both_words, \ + receive_map_big_endian_large_32_bit_spanning_both_words, \ + receive_map_big_endian_large_32_bit_mostly_in_first_word, \ + receive_map_big_endian_large_32_bit_mostly_in_second_word, + +#endif // CAN_SIGNED + +REGISTER_TEST( + CanMapTest, + send_map_little_endian_byte_in_first_word, + send_map_little_endian_16_bit_in_first_word, + send_map_little_endian_32_bit_in_first_word, + send_map_little_endian_32_bit_in_second_word, + send_map_little_endian_negative_number_16_bit_in_first_word, + send_map_little_endian_negative_number_24_bit_in_first_word, + send_map_little_endian_negative_number_32_bit_in_first_word, + send_map_little_endian_negative_number_32_bit_in_second_word, + send_map_little_endian_negative_number_32_bit_spanning_both_words, + send_map_little_endian_negative_number_32_bit_mostly_in_first_word, + send_map_little_endian_negative_number_32_bit_mostly_in_second_word, + send_map_little_endian_negative_number_16_bit_at_end_of_frame, + send_map_big_endian_byte_in_first_word, + send_map_big_endian_16_bit_in_first_word, + send_map_big_endian_32_bit_in_first_word, + send_map_big_endian_negative_byte_in_first_word, + send_map_big_endian_negative_16_bit_in_first_word, + send_map_big_endian_negative_24_bit_in_first_word, + send_map_big_endian_negative_32_bit_in_first_word, + send_map_big_endian_negative_byte_in_second_word, + send_map_big_endian_negative_16_bit_in_second_word, + send_map_big_endian_negative_24_bit_in_second_word, + send_map_big_endian_negative_32_bit_in_second_word, + send_map_big_endian_negative_number_24_bit_at_end_of_frame, + send_map_big_endian_negative_16_bit_spanning_both_words, + send_map_big_endian_negative_32_bit_spanning_both_words, + send_map_big_endian_negative_32_bit_mostly_in_first_word, + send_map_big_endian_negative_32_bit_mostly_in_second_word, + receive_map_little_endian_byte_in_first_word, + receive_map_little_endian_16_bit_in_first_word, + receive_map_little_endian_32_bit_in_first_word, + receive_map_little_endian_32_bit_in_second_word, + receive_map_big_endian_byte_in_first_word, + receive_map_big_endian_16_bit_in_first_word, + receive_map_big_endian_31_bit_in_first_word, + receive_map_big_endian_32_bit_in_first_word, + receive_map_little_endian_single_bit_first_bit, + receive_map_big_endian_single_bit_first_bit, + receive_map_little_endian_single_bit_last_bit, + receive_map_big_endian_single_bit_last_bit, + receive_map_little_endian_single_bit_in_first_word, + receive_map_little_endian_single_bit_in_second_word, + fail_to_map_with_invalid_can_id, + fail_to_map_with_invalid_little_endian_offset, + fail_to_map_with_invalid_little_endian_length, + fail_to_map_with_invalid_little_endian_total_struct_offset, + fail_to_map_with_invalid_big_endian_offset, + fail_to_map_with_invalid_big_endian_length, + fail_to_map_with_invalid_big_endian_total_struct_offset, + RECEIVE_TESTS); diff --git a/test/test_fp.cpp b/test/test_fp.cpp new file mode 100644 index 0000000..04f6bc8 --- /dev/null +++ b/test/test_fp.cpp @@ -0,0 +1,68 @@ +/* + * This file is part of the tumanako_vc project. + * + * Copyright (C) 2010 Johannes Huebner + * Copyright (C) 2010 Edward Cheeseman + * Copyright (C) 2009 Uwe Hermann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "my_fp.h" +#include "my_math.h" +#include "sine_core.h" +#include "test.h" +#include "string.h" + +class FPTest: public UnitTest +{ + public: + FPTest(const std::list* cases): UnitTest(cases) {} +}; + +static void TestMacros() +{ + ASSERT(FP_MUL(FP_FROMFLT(5.5), FP_FROMFLT(2.03125)) == FP_FROMFLT(5.5 * 2.03125)); + ASSERT(FP_MUL(FP_FROMFLT(5.5), FP_FROMFLT(2.03225)) == FP_FROMFLT(5.5 * 2.03125)); + ASSERT(FP_DIV(FP_FROMFLT(5.5), FP_FROMFLT(2.03125)) == FP_FROMFLT(5.5 / 2.03125)); +} + +static void TestItoa() +{ + char buf[10]; + ASSERT(strcmp(fp_itoa(buf, FP_FROMFLT(2.03125)), "2.03") == 0); + ASSERT(strcmp(fp_itoa(buf, FP_FROMFLT(-2.125)), "-2.12") == 0); + ASSERT(strcmp(fp_itoa(buf, FP_FROMFLT(2.15624)), "2.12") == 0); + ASSERT(strcmp(fp_itoa(buf, FP_FROMFLT(2.15625)), "2.15") == 0); +} + +static void TestAtoi() +{ + ASSERT(fp_atoi("-2.5", 5) == FP_FROMFLT(-2.5)); + ASSERT(fp_atoi("2.155", 5) == FP_FROMFLT(2.16)); +} + +static void TestMedian3() +{ + ASSERT(MEDIAN3(1,2,3) == 2); + ASSERT(MEDIAN3(3,2,1) == 2); + ASSERT(MEDIAN3(1,3,2) == 2); + ASSERT(MEDIAN3(2,3,1) == 2); + ASSERT(MEDIAN3(2,1,3) == 2); +} + +//This line registers the test +REGISTER_TEST(FPTest, TestMacros, TestItoa, TestAtoi, TestMedian3); + + diff --git a/test/test_fu.cpp b/test/test_fu.cpp new file mode 100644 index 0000000..161a405 --- /dev/null +++ b/test/test_fu.cpp @@ -0,0 +1,75 @@ +/* + * This file is part of the tumanako_vc project. + * + * Copyright (C) 2010 Johannes Huebner + * Copyright (C) 2010 Edward Cheeseman + * Copyright (C) 2009 Uwe Hermann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "fu.h" +#include "my_fp.h" +#include "test.h" + +class FUTest: public UnitTest +{ + public: + FUTest(const std::list* cases): UnitTest(cases) {} +}; + +static void Setup(float fweak, int boost) +{ + MotorVoltage::SetMaxAmp(10000); + MotorVoltage::SetBoost(boost); + MotorVoltage::SetWeakeningFrq(fweak); +} + +static void TestBoost1() +{ + Setup(10, 1000); + ASSERT(MotorVoltage::GetAmp(FP_FROMINT(1))==1900); +} + +static void TestBoost2() +{ + Setup(10, 1000); + ASSERT(MotorVoltage::GetAmp(FP_FROMFLT(0.1))==0); +} + +static void TestFU1() +{ + Setup(10, 0); + ASSERT(MotorVoltage::GetAmp(FP_FROMFLT(5))==(5.0/10.0f * 10000)); +} + +static void TestFU2() +{ + Setup(10, 1000); + ASSERT(MotorVoltage::GetAmp(FP_FROMFLT(5))==(1000 + 5.0/10.0f * (10000-1000))); + ASSERT(MotorVoltage::GetAmp(FP_FROMFLT(9.5))==(1000 + 9.5/10.0f * (10000-1000))); + ASSERT(MotorVoltage::GetAmp(FP_FROMFLT(10))==10000); + ASSERT(MotorVoltage::GetAmp(FP_FROMFLT(100))==10000); +} + +static void TestFUPerc() +{ + Setup(10, 1000); + ASSERT(MotorVoltage::GetAmpPerc(FP_FROMFLT(5), FP_FROMFLT(50))==((1000 + 5.0/10.0f * (10000-1000))/2)); + ASSERT(MotorVoltage::GetAmpPerc(FP_FROMFLT(22), FP_FROMFLT(50))==10000); +} + +//This line registers the test +REGISTER_TEST(FUTest, TestBoost1, TestBoost2, TestFU1, TestFU2, TestFUPerc); + diff --git a/test/test_main.cpp b/test/test_main.cpp new file mode 100644 index 0000000..2f083f4 --- /dev/null +++ b/test/test_main.cpp @@ -0,0 +1,61 @@ +/* + * This file is part of the tumanako_vc project. + * + * Copyright (C) 2010 Johannes Huebner + * Copyright (C) 2010 Edward Cheeseman + * Copyright (C) 2009 Uwe Hermann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include "test.h" + +using namespace std; + +int _failedAssertions = 0; +static int testIdx = 0; +static list testList; + +int main() +{ + cout << "Starting unit Tests" << endl; + + for (UnitTest* currentTest: testList) + { + currentTest->TestSetup(); + + for (VoidFunction testCase: currentTest->GetCases()) + { + currentTest->TestCaseSetup(); + testCase(); + } + } + + if (_failedAssertions > 0) + { + cout << _failedAssertions << " assertions failed" << endl; + return -1; + } + + cout << "All tests passed" << endl; + + return 0; +} + +UnitTest::UnitTest(const list* cases) +: _cases(cases) +{ + testList.push_back(this); +}