How to access functions from the main crate when writing integration tests? - unit-testing

When creating a project with a test like so:
cargo init --bin projectname
mkdir projectname/tests
echo "extern crate projectname;" > projectname/tests/test.rs
cd projectname/
cargo build
I get this error when testing:
cargo test
Compiling projectname v0.1.0 (file:///home/username/Lab/projectname)
error[E0463]: can't find crate for `projectname`
--> tests/test.rs:1:1
|
1 | extern crate projectname;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate
How can I access the functions in ´projectname/src/main.rs´ from projectname/tests/test.rs?

How can I access functions in ´projectname/src/main.rs´ from projectname/tests/test.rs?
You cannot.
A binary cannot be used a an external crate (the same way as you can't use a ELF binary as a shared object/library)
You just have to change your initialisation to
cargo init --lib projectname
or rename your main.rs to lib.rs
If you really want to stick with a main, you may look at Rust package with both a library and a binary?.

Related

compiler can't find header file on pre-build stage with target_include_directories

Language server (clangd) says he can't find lib.h when I trying to include header in lib_gtests.cpp : #include "lib.h".
But when I compile everything is OK and lib_gtests can find a header file.
I may miss something in CMake or I should use an IDE because they are more "smart" and are able to find these pre-build dependencies
project
|
|-------> src
| |----> main.cpp
| |----> lib
| |----> lib.cpp
| |----> lib.h
|
|
|-------> tests
| |----> lib
| |----> lib_gtests.cpp
|
|
|---->CMakeLists.txt
CMakeLists.txt
add_executable(unit_tests
tests/lib/lib_gtests.cpp
)
target_link_libraries(unit_tests PRIVATE lib)
target_include_directories(unit_tests PRIVATE ${CMAKE_SOURCE_DIR}/src/lib)
sorry for a dumb question, I'm new to C++ and CMake
and sorry for my English in advance
and could I ask for an advice for sources on CMake (not the official docs, they're scary)
use an IDE (it'll make all these included directories on compile stage visible for a language server by itself, I guess this is why everything works fine for me)
or, if you really want to use some text editor with a language server (for instance NeoVim, as I do. Actually, it's such a pain in the butt but I like this) use ccls as a language server and tell CMake to create compile_commands.json file and create a link to this file in your project directory.
cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=YES
ln -s build/compile_commands .

Using dune + qtest in a multi-file project

Let me begin by saying that I'm a complete beginner in OCaml, so if I seem to have made some weird choices, I'm most likely not aware of them being a choice.
I am trying to get unit testing working in my project. After some searching I settled on qtest.lib.
I've set up my project as follows:
$ mkdir mylib
$ cd mylib
$ dune init lib mylib
In the dune file, I've written:
(library
(name mylib)
(inline_tests (backend qtest.lib)))
In mylib.ml, I've put the following code:
let foo x = x + 2
(*$T foo
foo 2 = 4
*)
At this point, everything works as expected:
$ dune runtest
Info: Creating file dune-project with this contents:
| (lang dune 2.9)
inline_test_runner_mylib alias runtest
random seed: 425752161
[1 / 1] >foo>mylib.ml:4 *
[1 / 1] >foo>;32;1mSUCCESS
The issues start if I try to introduce another file into the project. I created helper.ml with the following contents:
let bar x = 3 * x
(*$T bar
bar 3 = 9
*)
Now, dune runtest errors out with
$ dune runtest
File "mylib.ml", line 11, characters 5-11:
Error: Unbound module Helper
In some of my other attempts at reproducing it, the file mentioned was instead _build/default/.mylib.inline-tests/inline_test_runner_mylib.ml-gen.
I first assumed that this means I'm organizing my files incorrectly. However, I can access Helper.bar within mylib.ml:
$ cat mylib.ml
let foo x = Helper.bar (x + 2)
$ dune build # no errors
Thus I have no idea what the problem could be here. What's going on?
Strangely it looks like you need to put
(modules)
in your dune file (as you can see here)
Your dune file will look like:
(library
(name mylib)
(modules)
(inline_tests (backend qtest.lib)))
Dune wraps library by default in a module with the same name as the library. For instance, defining a mylib library with
a.ml
b.ml
will create a Mylib module with A and B as submodule.
However, if you define a mylib module by hand, dune consider that this module is the entry point of your library, and it is your responsibility to expose all visible submodule.
Thus if you define mylib.ml as:
let x = 0
you are explicitly hiding the Helper module.
The simpler option here is probably to rename the Mylib module.
Another issue is that the qtest backend does not seem aware of dune wrapping libraries. A potential workaround is to define your own inline test backend and add an open Mylib to the preamble
(library (name myqtest)
(modules)
(inline_tests.backend
(runner_libraries qcheck ounit2 bytes)
(generate_runner (run qtest extract --preamble "open Myib" --quiet %{impl-files} %{intf-files}))
)
)
(library (name mylib)
(inline_tests
(backend myqtest)
)
)
(I hope that I am missing an option and that there is a simpler solution to send a flag to the runner generator)

CMake, subdirectories and Google Test (mock) not linking properly. Tests not found

I have a directory structure like this:
ProjectFolder
\--googletest (default google test layout)
|--source
| \--test
| | \--CMakeLists.txt
| | |--test_Some.cpp
| |--CMakeLists.txt
| |--some.cpp
| |--some.hpp
|--CMakeLists.txt
|--main.cpp (normal main)
|--maintest.cpp (empty)
ProjectFolder/CMakeLists.txt:
project(ProjectFolder)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/source
${gmock_SOURCE_DIR}/include
${gtest_SOURCE_DIR}/include
)
add_subdirectory(googletest)
add_subdirectory(source)
add_executable(ProjectFolderTest maintest.cpp)
target_link_libraries(ProjectFolderTest source sourceTest gmock gmock_main)
ProjectFolder/source/CMakeLists.txt
add_library(source some.cpp)
add_subdirectory(test)
ProjectFolder/source/test/CMakeLists.txt
add_library(sourceTest test_Some.cpp)
This sadly does not work. sourceTest is never really linked (it seems). When I remove the source library from target_link_libraries I normally would expect undefined references but even that doesn't happen.
When I add a #warning to test_Some.cpp I do see the warning passing by.
If I remove sourceTest from target_link_libraries and add a hard reference to add_executable directly to source/test/test_Some.cpp I do get tests added and they run just fine.
How do I get this to work using add_subdirectories without having to add all the files in one CMakeLists.txt? Or what should I be doing different overall?
(Just trying to learn how to use CMake). I can't use the proposed option to download googletest from the interwebs using ExternalProject because I am evaluating CMake for a project where we have to have specific versions inside our own VCS.
I am not really sure, but it might be connected to this issue.
Since you didn't post your tests source code I cannot really tell, if your solution should work or not, however I strongly recommend the following structure:
ProjectFolder
|--googletest (default google test layout)
\--source
| |--CMakeLists.txt
| |--main.cpp (normal main)
| |--some.cpp
| |--some.hpp
\--test
| |--CMakeLists.txt
| |--maintest.cpp (not empty)
| |--test_Some.cpp
|--CMakeLists.txt
Then, the test's CMakeLists.txt might look like this:
enable_testing()
set(MY_TEST_FILES maintest.cpp
test_Some.cpp)
add_executable(ut-myTest ${MY_TEST_FILES})
target_link_libraries(ut-myTest gmock SomeOtherLibrary)
add_test(MyTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ut-myTest)
And your test's main should look like this:
#include "gmock/gmock.h"
int main(int argc, char **argv) {
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
EDIT =====
Just a clarification: add_test directive just allows you to run your tests with make test. You can still use your tests without it, just by executing the binary ut-myTest.

How to avoid mixing test and production code using GoogleTest?

I am starting to use GoogleTest. It seems that it needs a main file for running the tests:
Separate test cases across multiple files in google test
But currently in my demo application I already have a main file:
src/
-> MyType.h
-> main.cpp
-> Makefile
Which will eventually be my "production" application. I don't want to clutter that with gtest includes, macros etc.
Should I just create another main.cpp file in another folder e.g.: test/ that will contain all the specific gtest configuration so I would end up with:
src/
-> MyType.h
-> main.cpp
-> Makefile // Makefile for producing production code/binaries
Test/
-> MyTypeTest.h // Unittest for MyType
-> main.cpp // The "Test runner"
-> Makefile // Makefile for producing test executable
EDIT:
Found this based on cmake:
http://www.kaizou.org/2014/11/gtest-cmake/
which seems to be exactly what I am looking for.
The most sensible approach to this is to have a library for your production code and then two executables, one for production and another one for tests:
|-lib/
| |-Makefile
| |-mytype.h
| `-mytype.cpp
|-app/
| |-Makefile
| `-main.cpp
`-test/
|-Makefile
`-mytypetest.cpp
Notice that gtest distribution provides the gtest library and a gtest_main library with the standard main function for your test executable. So unless you need a custom main (rare case) you don't need to provide a main.cpp for your tests and can simply link against gtest_main, e.g. $(CC) mytypetest.cpp -o apptests -lapplib -lgtest_main -lgtest.
The library approach involves slightly more complex Makefiles, but it pays off in compilation time, since not having it implies you need to compile mytype.cpp once for production application and once for test executable.
There are probably a lot of ways to do this, but generally speaking, yes, you should add a test-specific main function to your project. This makes compilation a little bit more complex since you'll have to produce two separate binaries (one for your application and another for your tests) but this is a fairly typical setup.
I'd simply add a test.cpp file with a main and create a test target in my makefile so that I could either make - to build my production code - or make test - to build the tests. In actual projects I use cmake in very similar fashion (I sometimes bundle all common dependencies in a core.a library and then link both main and test against it).

Modular C++ Project Build with CMake

I'm trying to find a way to build a big modular C++ project with CMake.
The structure of the project is the following:
--project_root
--src
--folder_1
--source_1.h
--source_1.cc
--test_source_1.cc // file containing a main with unit tests
--folder_2
--source_2.h
--source_2.cc
--test_source_2.cc // file containing a main with unit tests
--folder_3
...
And so on.
Each folder represent a project module and each module might depend on other modules, so for example source_1.h may include source_2.h.
Every module folder may also contains a test file so the whole project will have multiple executables.
How can I build the whole project with CMake? How should I write my CMakeLists.txt file?
Thank you a lot.
There are many, many examples out there of how to structure CMake projects for C++, many of which are referenced by the tutorial #user2485710 suggested in his comment, so I'm not going to go super in-depth here, but I'll at least give you a good starting point based on the way you want to lay out you folder structure.
The nice thing about CMake is that it can essentially do a tree-decent using the add_subdirectory command. This lets us easily divide up our CMake code to only do what is required at any specific directory level. In otherwords, each CMakeLists.txt file should only do the minimal amount of work needed to properly set up things at the current depth in the directory tree. In you example, your CMake tree might look like this:
--project_root
--src
--CMakeLists.txt
--folder_1
--CMakeLists.txt
--source_1.h
--source_1.cc
--test_source_1.cc // file containing a main with unit tests
--folder_2
--CMakeLists.txt
--source_2.h
--source_2.cc
--test_source_2.cc // file containing a main with unit tests
...
In src/CMakeLists.txt you do all of your project-level initialization, I.E. find_package, setting up your include-path, etc. Then you simply add the following at the end:
add_subdirectory(folder_1)
add_subdirectory(folder_2)
...
This tells CMake that it should look in those folders for additional stuff to do. Now in src/folder_1/CMakeLists.txt, we do the actual work of whatever combination of add_executable and add_library you need to properly build source_1.cc and test_source_1.cc, and likewise in src/folder_2/CMakeLists.txt for source_2.cc, etc.
The other nice thing is that any CMake variables you set higher up the tree are propagated down through add_subdirectory. So, for example, in src/CMakeLists.txt you can check for some sort of 'build unit-test' flag and set the CMake variable there, and then all you have to do in the other CMakeLists.txt files is check for that variable. This can also be super useful to do if you have a project where CMake is dynamically generating header files for you based on checking environment variables for path-names and the like.
If the structure of the project is well-regulated, you could write custom macros or function of cmake to define the modules and their dependencies.
The cmake scripts in OpenCV project is a good reference:
/libs/opencv-2.4.8/sources/
|+cmake/
|+doc/
|~modules/
| |+core/
| | |+doc/
| | |+include/
| | |+perf/
| | |+src/
| | |+test/
| | `-CMakeLists.txt
| |~imgproc/
| | |+doc/
| | |+include/
| | |+perf/
| | |+src/
| | |+test/
| | `-CMakeLists.txt
| |+ml/
| |+...
| |-CMakeLists.txt
|-CMakeLists.txt
root/modules/imgproc/CMakeLists.txt
set(the_description "Image Processing")
ocv_define_module(imgproc opencv_core)
You will need a CMakeLists.txt in each folder where building will occur.
project() is used to set the name of your overall project.
add_subdirectory() is used to command the configuration to process the CMakeLists.txt in that directory.
add_executable() is used to create an executable from included sources.
add_library() is used to create a static or dynamic library that can be added to executables or libraries as a dependency.
Using a build script (.bat, .cmd, or .sh) will allow you to automate some of the cmake process, such as setting up an out-of-source configuration or build.
You should look up the documentation for these commands on the cmake website, https://cmake.org/cmake/help/latest/