From 15e20f4a160af082f1b2435a3bc8ff4c023fdcef Mon Sep 17 00:00:00 2001 From: Michael Karlesky Date: Thu, 8 Oct 2026 13:11:02 -0400 Subject: [PATCH] Add :mock_name_include_folder runner option The runner names each mock's Init, Verify and Destroy calls by the mock's filename alone. Two mocks sharing a filename in different folders therefore collapse to the same calls. With :mock_name_include_folder enabled the runner keeps the folder a mock is included from, so uart/MockConfig.h yields uart_MockConfig_Init(). CMock gains a matching option that names its generated functions the same way. The option defaults to off and leaves existing runners unchanged. Co-Authored-By: Claude Opus 5.5 --- auto/generate_test_runner.rb | 6 ++- docs/UnityHelperScriptsGuide.md | 9 ++++ test/testdata/spi/MockConfig.h | 19 +++++++++ .../testRunnerGeneratorWithSameNamedMocks.c | 42 +++++++++++++++++++ test/testdata/uart/MockConfig.h | 19 +++++++++ test/tests/test_generate_test_runner.rb | 29 +++++++++++++ 6 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 test/testdata/spi/MockConfig.h create mode 100644 test/testdata/testRunnerGeneratorWithSameNamedMocks.c create mode 100644 test/testdata/uart/MockConfig.h diff --git a/auto/generate_test_runner.rb b/auto/generate_test_runner.rb index 2589311c0..af3b98aea 100755 --- a/auto/generate_test_runner.rb +++ b/auto/generate_test_runner.rb @@ -36,6 +36,7 @@ def self.default_options test_prefix: 'test|spec|should', mock_prefix: 'Mock', mock_suffix: '', + mock_name_include_folder: false, setup_name: 'setUp', teardown_name: 'tearDown', test_reset_name: 'resetTest', @@ -339,7 +340,10 @@ def create_mock_management(output, mock_headers) output.puts(' GlobalOrderError = NULL;') end - mocks = mock_headers.map { |mock| File.basename(mock, '.*') } + # optionally keep the folder a mock is included from to match cmock's naming + mocks = mock_headers.map do |mock| + @options[:mock_name_include_folder] ? mock.sub(/\A(?:\.[\/\\])+/, '').sub(/\.[^.\/\\]*\z/, '') : File.basename(mock, '.*') + end mocks.each do |mock| mock_clean = TypeSanitizer.sanitize_c_identifier(mock) output.puts(" #{mock_clean}_Init();") diff --git a/docs/UnityHelperScriptsGuide.md b/docs/UnityHelperScriptsGuide.md index 30841cf94..33f4bea81 100644 --- a/docs/UnityHelperScriptsGuide.md +++ b/docs/UnityHelperScriptsGuide.md @@ -157,6 +157,15 @@ This option can also be specified at the command prompt as `--externc` Unity automatically generates calls to Init, Verify and Destroy for every file included in the main test file that starts with the given mock prefix and ends with the given mock suffix, file extension not included. By default, Unity assumes a `Mock` prefix and no suffix. +##### `:mock_name_include_folder` + +This option names each mock's Init, Verify and Destroy calls by the folder a mock is included from as well as its filename. +A mock included as `uart/MockConfig.h` gets a call to `uart_MockConfig_Init()` rather than `MockConfig_Init()`. +This keeps two mocks of the same filename in different folders distinct within one test. +CMock offers an option of the same name that names its generated functions the same way, and the two must agree. +If you provide the same YAML to the generator as used in CMock's configuration, you've already configured the generator properly. +By default, Unity names mocks by filename alone. + ##### `:plugins` This option specifies an array of plugins to be used (of course, the array can contain only a single plugin). diff --git a/test/testdata/spi/MockConfig.h b/test/testdata/spi/MockConfig.h new file mode 100644 index 000000000..363c4fd30 --- /dev/null +++ b/test/testdata/spi/MockConfig.h @@ -0,0 +1,19 @@ +/* ========================================================================= + Unity - A Test Framework for C + ThrowTheSwitch.org + Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams + SPDX-License-Identifier: MIT +========================================================================= */ + +#ifndef SPI_MOCK_CONFIG_H +#define SPI_MOCK_CONFIG_H + +extern int spi_MockConfig_Init_Counter; +extern int spi_MockConfig_Verify_Counter; +extern int spi_MockConfig_Destroy_Counter; + +void spi_MockConfig_Init(void); +void spi_MockConfig_Verify(void); +void spi_MockConfig_Destroy(void); + +#endif diff --git a/test/testdata/testRunnerGeneratorWithSameNamedMocks.c b/test/testdata/testRunnerGeneratorWithSameNamedMocks.c new file mode 100644 index 000000000..b9d7028e2 --- /dev/null +++ b/test/testdata/testRunnerGeneratorWithSameNamedMocks.c @@ -0,0 +1,42 @@ +/* ========================================================================= + Unity - A Test Framework for C + ThrowTheSwitch.org + Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams + SPDX-License-Identifier: MIT +========================================================================= */ + +#include "unity.h" +#include "uart/MockConfig.h" +#include "spi/MockConfig.h" + +/* Stand-ins for two mocks sharing a filename, named by the folder each is included by */ +int uart_MockConfig_Init_Counter = 0; +int uart_MockConfig_Verify_Counter = 0; +int uart_MockConfig_Destroy_Counter = 0; +int spi_MockConfig_Init_Counter = 0; +int spi_MockConfig_Verify_Counter = 0; +int spi_MockConfig_Destroy_Counter = 0; + +void uart_MockConfig_Init(void) { uart_MockConfig_Init_Counter++; } +void uart_MockConfig_Verify(void) { uart_MockConfig_Verify_Counter++; } +void uart_MockConfig_Destroy(void) { uart_MockConfig_Destroy_Counter++; } +void spi_MockConfig_Init(void) { spi_MockConfig_Init_Counter++; } +void spi_MockConfig_Verify(void) { spi_MockConfig_Verify_Counter++; } +void spi_MockConfig_Destroy(void) { spi_MockConfig_Destroy_Counter++; } + +void setUp(void) {} +void tearDown(void) {} + +void test_ShouldInitEachSameNamedMock(void) +{ + TEST_ASSERT_EQUAL_MESSAGE(1, uart_MockConfig_Init_Counter, "First Mock Init Should Be Called"); + TEST_ASSERT_EQUAL_MESSAGE(1, spi_MockConfig_Init_Counter, "Second Mock Init Should Be Called"); +} + +void test_ShouldVerifyAndDestroyEachSameNamedMock(void) +{ + TEST_ASSERT_EQUAL_MESSAGE(1, uart_MockConfig_Verify_Counter, "First Mock Verify Should Be Called"); + TEST_ASSERT_EQUAL_MESSAGE(1, spi_MockConfig_Verify_Counter, "Second Mock Verify Should Be Called"); + TEST_ASSERT_EQUAL_MESSAGE(1, uart_MockConfig_Destroy_Counter, "First Mock Destroy Should Be Called"); + TEST_ASSERT_EQUAL_MESSAGE(1, spi_MockConfig_Destroy_Counter, "Second Mock Destroy Should Be Called"); +} diff --git a/test/testdata/uart/MockConfig.h b/test/testdata/uart/MockConfig.h new file mode 100644 index 000000000..532766546 --- /dev/null +++ b/test/testdata/uart/MockConfig.h @@ -0,0 +1,19 @@ +/* ========================================================================= + Unity - A Test Framework for C + ThrowTheSwitch.org + Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams + SPDX-License-Identifier: MIT +========================================================================= */ + +#ifndef UART_MOCK_CONFIG_H +#define UART_MOCK_CONFIG_H + +extern int uart_MockConfig_Init_Counter; +extern int uart_MockConfig_Verify_Counter; +extern int uart_MockConfig_Destroy_Counter; + +void uart_MockConfig_Init(void); +void uart_MockConfig_Verify(void); +void uart_MockConfig_Destroy(void); + +#endif diff --git a/test/tests/test_generate_test_runner.rb b/test/tests/test_generate_test_runner.rb index 3d05020d7..9a7f21e3f 100644 --- a/test/tests/test_generate_test_runner.rb +++ b/test/tests/test_generate_test_runner.rb @@ -71,6 +71,21 @@ } }, + { :name => 'SameNamedMocksByFolder', + :testfile => 'testdata/testRunnerGeneratorWithSameNamedMocks.c', + :testdefines => ['TEST'], + :options => { + :mock_name_include_folder => true, + }, + :expected => { + :to_pass => [ 'test_ShouldInitEachSameNamedMock', + 'test_ShouldVerifyAndDestroyEachSameNamedMock', + ], + :to_fail => [ ], + :to_ignore => [ ], + } + }, + { :name => 'ShorterFilterOfJustTest', :testfile => 'testdata/testRunnerGenerator.c', :testdefines => ['TEST'], @@ -1383,6 +1398,20 @@ def verify_number(expected, expression, output) $generate_test_runner_tests += 1 end +should 'NameMocksByBasenameWithoutFolderOption' do + runner_name = OUT_FILE + 'SameNamedMocksByBasename_runner.c' + UnityTestRunnerGenerator.new({}).run('testdata/testRunnerGeneratorWithSameNamedMocks.c', runner_name) + + runner = File.read(runner_name) + if runner.scan(' MockConfig_Init();').length == 2 && !runner.include?('uart_MockConfig_Init();') + report 'Runner_NameMocksByBasenameWithoutFolderOption:PASS' + else + report 'Runner_NameMocksByBasenameWithoutFolderOption:FAIL' + $generate_test_runner_failures += 1 + end + $generate_test_runner_tests += 1 +end + RUNNER_TESTS.each do |testset| basename = File.basename(testset[:testfile], C_EXTENSION) testset_name = "Runner_#{basename}_#{testset[:name]}"