Skip to content

Additional test source files not being included if they are nested in a different folder #1124

@rolandsfr

Description

@rolandsfr

Description:
Necessary source file is not being linked (and compiled for that matter) for a test file whose source file use other source file which is nested deeper in the project tree. To illustrate this, I have provided a minimal example.

Project stcture:

├── compile_commands.json
├── include
│   └── parent.h
├── project.yml
├── src
│   └── parent
│       ├── child
│       │   ├── child.c
│       │   ├── child.h
│       │   └── child.test.c
│       ├── parent.c
│       └── parent.test.c
└── tests_build
    ├── artifacts
    │   └── test
    ├── logs
    ├── test
    │   ├── cache
    │   ├── dependencies
    │   │   ├── child_runner.d
    │   │   ├── child.d
    │   │   ├── child.test_runner.d
    │   │   ├── child.test.d
    │   │   ├── parent_runner.d
    │   │   ├── parent.d
    │   │   ├── parent.test_runner.d
    │   │   ├── parent.test.d
    │   │   └── unity.d
    │   ├── mocks
    │   │   ├── child
    │   │   ├── child.test
    │   │   ├── parent
    │   │   └── parent.test
    │   ├── out
    │   │   ├── child
    │   │   │   ├── child_runner.o
    │   │   │   ├── child.o
    │   │   │   ├── child.out
    │   │   │   └── unity.o
    │   │   ├── child.test
    │   │   │   ├── child.o
    │   │   │   ├── child.test_runner.o
    │   │   │   ├── child.test.o
    │   │   │   ├── child.test.out
    │   │   │   └── unity.o
    │   │   ├── parent
    │   │   │   ├── parent_runner.o
    │   │   │   ├── parent.o
    │   │   │   └── unity.o
    │   │   └── parent.test
    │   │       ├── child.o
    │   │       ├── parent.o
    │   │       ├── parent.test_runner.o
    │   │       ├── parent.test.o
    │   │       ├── parent.test.out
    │   │       └── unity.o
    │   ├── results
    │   └── runners
    │       ├── child_runner.c
    │       ├── child.test_runner.c
    │       ├── parent_runner.c
    │       └── parent.test_runner.c
    └── vendor
        ├── cmock
        │   └── src
        │       ├── cmock_internals.h
        │       ├── cmock.c
        │       ├── cmock.h
        │       └── meson.build
        └── unity
            └── src
                ├── meson.build
                ├── unity_internals.h
                ├── unity.c
                └── unity.h

child.c:

int mul(int a, int b) {
    return a * b;
}

child.test.c

#include "unity.h"
#include "child.h"

void test_mul(void)
{
    TEST_ASSERT_EQUAL_INT(6, mul(3,2));
}

child.h

int mul(int a, int b);

parent.c

T

int sum(int a, int b) {
    return a + b;
}

int square(int n) {
    return mul(n,2);
}

parent.test.c

#include "unity.h"
#include "parent.h"

TEST_SOURCE_FILE("child/child.c");

void test_sum(void)
{
    TEST_ASSERT_EQUAL_INT(5, sum(3,2));
}

include/parent.h

int sum(int a, int b);
int square(int n);

Output from running ceedling test in root:

🚧 Loaded project configuration from working directory.
 > Using: <dir>/project.yml
 > Working directory: <dir>

Ceedling set up completed in 56 milliseconds

👟 Preparing Build Paths...

👟 Collecting Test Context
--------------------------
Parsing parent.c for build directive macros, #includes, and test case names...
Parsing parent.test.c for build directive macros, #includes, and test case names...
Parsing child.c for build directive macros, #includes, and test case names...
Parsing child.test.c for build directive macros, #includes, and test case names...

👟 Ingesting Test Configurations
--------------------------------
Collecting search paths, flags, and defines parent.c...
Collecting search paths, flags, and defines parent.test.c...
Collecting search paths, flags, and defines child.c...
Collecting search paths, flags, and defines child.test.c...

👟 Determining Files to be Generated...

👟 Mocking
----------

👟 Test Runners
---------------
Generating runner for parent.c...
Generating runner for parent.test.c...
Generating runner for child.c...
Generating runner for child.test.c...

👟 Determining Artifacts to Be Built...

👟 Building Objects
-------------------
Compiling parent.c...
Compiling parent::parent_runner.c...
Compiling parent::unity.c...
Compiling parent.test.c...
Compiling parent.test::child.c...
Compiling parent.test::parent.c...
Compiling parent.test::parent.test_runner.c...
Compiling parent.test::unity.c...
Compiling child.c...
Compiling child::child_runner.c...
Compiling child::unity.c...
Compiling child.test.c...
Compiling child.test::child.c...
Compiling child.test::child.test_runner.c...
Compiling child.test::unity.c...

👟 Building Test Executables
----------------------------
Linking parent.out...
ℹ️ NOTICE: If the linker reports missing symbols, the following may be to blame:
  1. This test lacks #include statements corresponding to needed source files (see note below).
  2. Project file paths omit source files corresponding to #include statements in this test.
  3. Complex macros, #ifdefs, etc. have obscured correct #include statements in this test.
  4. Your project is attempting to mix C++ and C file extensions (not supported).
  5. This test does not #include needed mocks (that triggers their generation).

NOTE: A test file directs the build of a test executable with #include statemetns:
  * By convention, Ceedling assumes header filenames correspond to source filenames.
  * Which code files to compile and link are determined by #include statements.
  * An #include statement convention directs the generation of mocks from header files.

OPTIONS:
  1. Doublecheck this test's #include statements.
  2. Simplify complex macros or fully specify symbols for this test in :project ↳ :defines.
  3. If no header file corresponds to the needed source file, use the TEST_SOURCE_FILE()
     build diective macro in this test to inject a source file into the build.

See the docs on conventions, paths, preprocessing, compilation symbols, and build directive macros.

🧨 EXCEPTION: 'Default Test Linker' (gcc) terminated with exit code [1] and output >>
Undefined symbols for architecture arm64:
  "_mul", referenced from:
      _square in parent.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
🌱 Ceedling could not complete operations because of errors

As you can see, parent.test.c file misses source from child.c

Please notice that I do not expect solutions in form "change your project structure" because I desire flexibility and freedom in my projects.
Theoretically TEST_SOURCE_FILE("child/child.c") should have done the thing but it didnt

project.yml:

:defines:
  :common: &common_defines []
  :test:
    - *common_defines
    - TEST
    - UNITY_INCLUDE_DOUBLE # Add it here without D prefix

:paths:
  :source:
    - src/**
  :include:
    - include
    - src
  :test:
    - src/** # unit tests live next to code

:flags:
  :preprocess:
    :compile: []

:project:
  # how to use ceedling. If you're not sure, leave this as `gem` and `?`

  :ceedling_version: 1.0.1

  # optional features. If you don't need them, keep them turned off for performance
  :use_mocks: TRUE
  :use_decorators: :auto # decorate Ceedling's output text. options are :auto, :all, or :none

  # tweak the way ceedling handles automatic tasks
  :build_root: tests_build
  :test_file_prefix: ""
  :test_file_suffix: ".test.c"
  :default_tasks:
    - test:all

  :release_build: FALSE

:plugins:
  :load_paths: []
  :enabled:
    - module_generator # handy for quickly creating source, header, and test templates
    - report_tests_pretty_stdout

:extension:
  :executable: .out

---
# Configuration Options specific to CMock. See CMock docs for details
# ====
# ====
# ====

:cmock:
  :mock_path: test/support/mocks
  :stub_path: test/support/stubs
  :mock_prefix: mock_
  :when_no_prototypes: :warn
  :enforce_strict_ordering: TRUE
  :plugins:
    :enabled:
      - :ignore
      - :callback
      - stdout_pretty_tests_report
      - module_generator
  :treat_as:
    uint8: HEX8
    uint16: HEX16
    uint32: UINT32
    int8: INT8
    bool: UINT8

################################################################
# PLUGIN CONFIGURATION
################################################################

#clang Add -gcov to the plugins list to make sure of the gcov plugin
# You will need to have gcov and gcovr both installed to make it work.
# For more information on these options, see docs in plugins/gcov
:gcov:
  :summaries: TRUE # Enable simple coverage summaries to console after tests
  :report_task: FALSE # Disabled dedicated report generation task (this enables automatic report generation)
  :utilities:
    - gcovr # Use gcovr to create the specified reports (default).
    #- ReportGenerator # Use ReportGenerator to create the specified reports.
  :reports: # Specify one or more reports to generate.
    # Make an HTML summary report.
    - HtmlBasic
  :gcovr:
    :html_medium_threshold: 75
    :html_high_threshold: 90

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions