Compiling shared unit test helper classes with Unity

I’m currently writing a pull parser library, and to simplify the single tests I made an util directory with some helper classes et all.
I then proceeded to create other test_* folders with the actual tests.
However, if I include any file in the utils folders I get linking errors, which if I read the documentation right, is due to platformio treating each test_ directory as an independent project.

So my question is how do I get to compile my test helper/utils classes while sharing them among all the separate tests?

Here’s my test directory for reference

$ tree test
test
├── json
│   ├── sample1.json
│   ├── sample2.json
│   ├── sample3.json
│   ├── sample4.json
│   └── sample5.json
├── README
├── test_emptyDocument
│   └── test_emptyDocument.cc
├── test_simpleNameValue
│   └── test_simpleNameValue.cc
└── test_util
    ├── EventRecordHandle.cc
    ├── EventRecordHandle.hh
    ├── NameRecord.cc
    ├── NameRecord.hh
    ├── Util.cc
    ├── Util.hh
    ├── ValueRecord.cc
    └── ValueRecord.hh

And the (long) error log

 *  Esecuzione dell'attività nella cartella MicroJson: platformio test 

Verbosity level can be increased via `-v, -vv, or -vvv` option
Collected 3 tests

Processing test_util in native environment
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Building...
/usr/bin/ld: /usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../lib/Scrt1.o: in function `_start':
/build/glibc/src/glibc/csu/../sysdeps/x86_64/start.S:103: undefined reference to `main'
collect2: error: ld returned 1 exit status
*** [.pio/build/native/program] Error 1
Building stage has failed, see errors above. Use `pio test -vvv` option to enable verbose output.
----------------------------------------------------------------------- native:test_util [ERRORED] Took 0.61 seconds -----------------------------------------------------------------------

Processing test_emptyDocument in native environment
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Building...
/usr/bin/ld: .pio/build/native/test/test_emptyDocument/test_emptyDocument.o: warning: relocation against `_ZTV17EventRecordHandle' in read-only section `.text._ZN17EventRecordHandleD2Ev[_ZN17EventRecordHandleD5Ev]'
/usr/bin/ld: .pio/build/native/test/test_emptyDocument/test_emptyDocument.o: in function `emptyStructureDocument(char const*, JsonStructure)':
test_emptyDocument.cc:(.text+0x35): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_emptyDocument.cc:(.text+0x78): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_emptyDocument.cc:(.text+0x87): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_emptyDocument.cc:(.text+0x10f): undefined reference to `NameRecord::toString[abi:cxx11]()'
/usr/bin/ld: test_emptyDocument.cc:(.text+0x159): undefined reference to `ValueRecord::toString[abi:cxx11]()'
/usr/bin/ld: test_emptyDocument.cc:(.text+0x1f9): undefined reference to `NameRecord::toString[abi:cxx11]()'
/usr/bin/ld: test_emptyDocument.cc:(.text+0x243): undefined reference to `ValueRecord::toString[abi:cxx11]()'
/usr/bin/ld: .pio/build/native/test/test_emptyDocument/test_emptyDocument.o: in function `EventRecordHandle::~EventRecordHandle()':
test_emptyDocument.cc:(.text._ZN17EventRecordHandleD2Ev[_ZN17EventRecordHandleD5Ev]+0xf): undefined reference to `vtable for EventRecordHandle'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
*** [.pio/build/native/program] Error 1
Building stage has failed, see errors above. Use `pio test -vvv` option to enable verbose output.
------------------------------------------------------------------- native:test_emptyDocument [ERRORED] Took 0.67 seconds -------------------------------------------------------------------

Processing test_simpleNameValue in native environment
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Building...
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: warning: relocation against `_ZTV17EventRecordHandle' in read-only section `.text._ZN17EventRecordHandleD2Ev[_ZN17EventRecordHandleD5Ev]'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleArrayString()':
test_simpleNameValue.cc:(.text+0x26): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x77): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x86): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xf0): undefined reference to `assertNameIndex(NameRecord const&, unsigned int)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x139): undefined reference to `assertValueString(ValueRecord const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleArrayInteger()':
test_simpleNameValue.cc:(.text+0x231): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x282): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x291): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x2fb): undefined reference to `assertNameIndex(NameRecord const&, unsigned int)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x310): undefined reference to `assertValueInteger(ValueRecord const&, int)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleArrayDouble()':
test_simpleNameValue.cc:(.text+0x3c2): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x413): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x422): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x48c): undefined reference to `assertNameIndex(NameRecord const&, unsigned int)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x4a8): undefined reference to `assertValueDouble(ValueRecord const&, double)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleArrayBool0()':
test_simpleNameValue.cc:(.text+0x55a): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x5ab): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x5ba): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x624): undefined reference to `assertNameIndex(NameRecord const&, unsigned int)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x639): undefined reference to `assertValueBool(ValueRecord const&, bool)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleArrayBool1()':
test_simpleNameValue.cc:(.text+0x6eb): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x73c): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x74b): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x7b5): undefined reference to `assertNameIndex(NameRecord const&, unsigned int)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x7ca): undefined reference to `assertValueBool(ValueRecord const&, bool)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleArrayNull()':
test_simpleNameValue.cc:(.text+0x87c): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x8cd): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x8dc): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x946): undefined reference to `assertNameIndex(NameRecord const&, unsigned int)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x956): undefined reference to `assertValueNull(ValueRecord const&)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleObjectString()':
test_simpleNameValue.cc:(.text+0xa08): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xa59): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xa68): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xb06): undefined reference to `assertNameKey(NameRecord const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xb6d): undefined reference to `assertValueString(ValueRecord const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleObjectInteger()':
test_simpleNameValue.cc:(.text+0xc8f): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xce0): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xcef): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xd8d): undefined reference to `assertNameKey(NameRecord const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xdc0): undefined reference to `assertValueInteger(ValueRecord const&, int)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleObjectDouble()':
test_simpleNameValue.cc:(.text+0xe9a): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xeeb): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xefa): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xf98): undefined reference to `assertNameKey(NameRecord const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0xfd2): undefined reference to `assertValueDouble(ValueRecord const&, double)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleObjectBool0()':
test_simpleNameValue.cc:(.text+0x10ac): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x10fd): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x110c): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x11aa): undefined reference to `assertNameKey(NameRecord const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x11dd): undefined reference to `assertValueBool(ValueRecord const&, bool)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleObjectBool1()':
test_simpleNameValue.cc:(.text+0x12b7): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x1308): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x1317): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x13b5): undefined reference to `assertNameKey(NameRecord const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x13e8): undefined reference to `assertValueBool(ValueRecord const&, bool)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `test_simpleObjectNull()':
test_simpleNameValue.cc:(.text+0x14c2): undefined reference to `EventRecordHandle::EventRecordHandle()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x1513): undefined reference to `parseDocumentWith(SlabParser&, EventRecordHandle&, char const*)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x1522): undefined reference to `EventRecordHandle::getEvents()'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x15c0): undefined reference to `assertNameKey(NameRecord const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/usr/bin/ld: test_simpleNameValue.cc:(.text+0x15ee): undefined reference to `assertValueNull(ValueRecord const&)'
/usr/bin/ld: .pio/build/native/test/test_simpleNameValue/test_simpleNameValue.o: in function `EventRecordHandle::~EventRecordHandle()':
test_simpleNameValue.cc:(.text._ZN17EventRecordHandleD2Ev[_ZN17EventRecordHandleD5Ev]+0xf): undefined reference to `vtable for EventRecordHandle'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
*** [.pio/build/native/program] Error 1
Building stage has failed, see errors above. Use `pio test -vvv` option to enable verbose output.
------------------------------------------------------------------ native:test_simpleNameValue [ERRORED] Took 0.53 seconds ------------------------------------------------------------------

========================================================================================== SUMMARY ==========================================================================================
Environment    Test                  Status    Duration
-------------  --------------------  --------  ------------
native         test_util             ERRORED   00:00:00.607
native         test_emptyDocument    ERRORED   00:00:00.668
native         test_simpleNameValue  ERRORED   00:00:00.527
========================================================================= 3 test cases: 0 succeeded in 00:00:01.802 =========================================================================

Here my platformio.ini

[platformio]
default_envs = native

[env:native]
platform = native
; Needed mostly for testing
test_build_src = true
build_flags =
    -D PLATFORM_NATIVE
    -D UNITY_INCLUDE_DOUBLE
    -D UNITY_DOUBLE_PRECISION=1e-12f

[env:uno]
platform = atmelavr
board = uno
framework = arduino

put them in the lib dir, filter out the “lib” for the “production” env

I’ve moved the utils in the lib folder and created a subproject, but now I can’t include the headers and object files from the main project

$ tree test
test
├── json
│   ├── sample1.json
│   ├── sample2.json
│   ├── sample3.json
│   ├── sample4.json
│   └── sample5.json
├── README
├── test_emptyDocument
│   └── test_emptyDocument.cc
└── test_simpleNameValue
    └── test_simpleNameValue.cc

3 directories, 8 files
$ tree lib
lib
├── README
└── test_util
    └── src
        ├── EventRecordHandle.cc
        ├── EventRecordHandle.hh
        ├── NameRecord.cc
        ├── NameRecord.hh
        ├── Util.cc
        ├── Util.hh
        ├── ValueRecord.cc
        └── ValueRecord.hh

2 directories, 9 files

Errors (parse is a subfolder in include):

Processing test_emptyDocument in native environment
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Building...
In file included from lib/test_util/src/EventRecordHandle.cc:1:
lib/test_util/src/EventRecordHandle.hh:10:10: fatal error: parse/JsonStructureType.hh: File o directory non esistente
   10 | #include <parse/JsonStructureType.hh>
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio/build/native/libe59/test_util/EventRecordHandle.o] Error 1
In file included from lib/test_util/src/Util.cc:1:
lib/test_util/src/Util.hh:8:10: fatal error: parse/SlabParser.hh: File o directory non esistente
    8 | #include <parse/SlabParser.hh>
      |          ^~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio/build/native/libe59/test_util/Util.o] Error 1
In file included from lib/test_util/src/ValueRecord.cc:1:
lib/test_util/src/ValueRecord.hh:6:10: fatal error: parse/handle/JsonStructure.hh: File o directory non esistente
    6 | #include <parse/handle/JsonStructure.hh>
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio/build/native/libe59/test_util/ValueRecord.o] Error 1
Building stage has failed, see errors above. Use `pio test -vvv` option to enable verbose output.
------------------------------------------------------------------- native:test_emptyDocument [ERRORED] Took 0.76 seconds -------------------------------------------------------------------

Processing test_simpleNameValue in native environment
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Building...
In file included from lib/test_util/src/EventRecordHandle.cc:1:
lib/test_util/src/EventRecordHandle.hh:10:10: fatal error: parse/JsonStructureType.hh: File o directory non esistente
   10 | #include <parse/JsonStructureType.hh>
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio/build/native/libe59/test_util/EventRecordHandle.o] Error 1
In file included from lib/test_util/src/ValueRecord.cc:1:
lib/test_util/src/ValueRecord.hh:6:10: fatal error: parse/handle/JsonStructure.hh: File o directory non esistente
    6 | #include <parse/handle/JsonStructure.hh>
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio/build/native/libe59/test_util/ValueRecord.o] Error 1
In file included from lib/test_util/src/Util.cc:1:
lib/test_util/src/Util.hh:8:10: fatal error: parse/SlabParser.hh: File o directory non esistente
    8 | #include <parse/SlabParser.hh>
      |          ^~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio/build/native/libe59/test_util/Util.o] Error 1
Building stage has failed, see errors above. Use `pio test -vvv` option to enable verbose output.
------------------------------------------------------------------ native:test_simpleNameValue [ERRORED] Took 1.04 seconds ------------------------------------------------------------------

========================================================================================== SUMMARY ==========================================================================================
Environment    Test                  Status    Duration
-------------  --------------------  --------  ------------
native         test_emptyDocument    ERRORED   00:00:00.756
native         test_simpleNameValue  ERRORED   00:00:01.042
========================================================================= 2 test cases: 0 succeeded in 00:00:01.798 =========================================================================

What do you mean? You created a new env?

What do you mean again? From the embedded env?


Esecuzione dell’attività nella cartella

Switch your system/IDE/everything to English – you will have a much easier time trying to google stuff ;p

  1. Make sure you have it in the lib_deps for the native env.
  2. Make sure that the lib is compatible with the native platform. If it is not, you may try off compat mode:

I’m trying to google this and find nothing. What is this lib?

As the calculator example shows, I created a new subfolder named test_util under lib, and and ulterior subfolder src inside of test_lib, where I put the .cc and ‘.hh’ files, ergo the my_platfornio_library_project/lib/test_util/src private library and “subproject”.

For the main library project that is the whole thing that I’m trying to test. The one whose files live in my_platformio_library_project/src and my_platformio_library_project/include.

That’s just a line from VS code not relevant to platformio. It says that it started a new activity in the folder, which is is exactly the same thing it says in any other project in any other programming language.

The one I’m writing, whose files reside in my_platformio_library_project/src and my_platformio_library_project/include.

I don’t understand the idea behind include folder. Mind that I’m c++ noob.

Move that to lib as well. Leave only main.cpp in the src dir.

I’ve had the same issue – a lot of code in src dir. Worked OK. Then I wanted to start testing. It turns out, those files are not found by pio test command. I know, wtf. I didn’t investigate much. Moving to lib worked for both “normal” and “test” build.