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); +}