Skip to content

Commit

Permalink
pw_unit_test: Support running tests in static libraries
Browse files Browse the repository at this point in the history
The PW_UNIT_TEST_LINK_FILE_CONTAINING_TEST(suite, test) macro refers to
a test in a static library so the linker will include it.

Fixed: b/233938994
Change-Id: Id7ae1c48047f2e5a45b68c312e6b49bf9921ca72
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/96040
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
  • Loading branch information
255 authored and CQ Bot Account committed May 27, 2022
1 parent 6ac788c commit fdc2388
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 13 deletions.
29 changes: 29 additions & 0 deletions pw_unit_test/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,35 @@ pw_cc_library(
],
)

pw_cc_library(
name = "static_library_support",
srcs = ["static_library_support.cc"],
hdrs = ["public/pw_unit_test/static_library_support.h"],
includes = ["public"],
deps = [":light"], # This library only works with the light backend
)

pw_cc_library(
name = "tests_in_archive",
srcs = [
"static_library_archived_tests.cc",
"static_library_missing_archived_tests.cc",
],
linkstatic = True,
visibility = ["//visibility:private"],
deps = [":pw_unit_test"],
)

pw_cc_test(
name = "static_library_support_test",
srcs = ["static_library_support_test.cc"],
deps = [
":static_library_support",
":tests_in_archive",
"//pw_assert",
],
)

pw_cc_test(
name = "framework_test",
srcs = ["framework_test.cc"],
Expand Down
52 changes: 42 additions & 10 deletions pw_unit_test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,17 @@ pool("unit_test_pool") {
depth = pw_unit_test_POOL_DEPTH
}

config("default_config") {
include_dirs = [
"public",
"public_overrides",
]
config("public_include_path") {
include_dirs = [ "public" ]
}

config("public_overrides_include_path") {
include_dirs = [ "public_overrides" ]
}

pw_source_set("config") {
public = [ "public/pw_unit_test/config.h" ]
public_configs = [ ":default_config" ]
public_configs = [ ":public_include_path" ]
public_deps = [
dir_pw_polyfill,
pw_unit_test_CONFIG,
Expand All @@ -68,7 +69,10 @@ pw_source_set("pw_unit_test") {

# Lightweight unit test backend that implements a subset of GoogleTest.
pw_source_set("light") {
public_configs = [ ":default_config" ]
public_configs = [
":public_include_path",
":public_overrides_include_path",
]
public_deps = [
":config",
":event_handler",
Expand All @@ -85,7 +89,7 @@ pw_source_set("light") {
}

pw_source_set("event_handler") {
public_configs = [ ":default_config" ]
public_configs = [ ":public_include_path" ]
public = [ "public/pw_unit_test/event_handler.h" ]
}

Expand Down Expand Up @@ -132,7 +136,7 @@ pw_source_set("logging_main") {
}

pw_source_set("rpc_service") {
public_configs = [ ":default_config" ]
public_configs = [ ":public_include_path" ]
public_deps = [
":pw_unit_test",
":unit_test_proto.pwpb",
Expand Down Expand Up @@ -160,6 +164,13 @@ pw_source_set("rpc_main") {
sources = [ "rpc_main.cc" ]
}

pw_source_set("static_library_support") {
public_configs = [ ":public_include_path" ]
public_deps = [ ":light" ] # This library only works with the light backend
public = [ "public/pw_unit_test/static_library_support.h" ]
sources = [ "static_library_support.cc" ]
}

pw_executable("test_rpc_server") {
sources = [ "test_rpc_server.cc" ]
deps = [
Expand All @@ -184,6 +195,27 @@ pw_test("framework_test") {
deps = [ dir_pw_assert ]
}

pw_static_library("tests_in_archive") {
sources = [
"static_library_archived_tests.cc",
"static_library_missing_archived_tests.cc",
]
deps = [ ":pw_unit_test" ]
visibility = [ ":*" ]
}

pw_test("static_library_support_test") {
sources = [ "static_library_support_test.cc" ]
deps = [
":static_library_support",
":tests_in_archive",
dir_pw_assert,
]
}

pw_test_group("tests") {
tests = [ ":framework_test" ]
tests = [
":framework_test",
":static_library_support_test",
]
}
9 changes: 9 additions & 0 deletions pw_unit_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ pw_add_module_library(pw_unit_test.light
public_overrides # pw_unit_test overrides the gtest/gtest.h header.
)

pw_add_module_library(pw_unit_test.static_library_support
HEADERS
public/pw_unit_test/static_library_support.h
SOURCES
static_library_support.cc
PUBLIC_DEPS
pw_unit_test.light
)

pw_add_module_library(pw_unit_test.main
SOURCES
simple_printing_main.cc
Expand Down
21 changes: 21 additions & 0 deletions pw_unit_test/docs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,27 @@ Currently, only a test suite filter is supported. This is set by calling
.. note::
Test filtering is only supported in C++17.

Tests in static libraries
=========================
The linker usually ignores tests linked through a static library (``.a`` file).
This is because test registration relies on the test instance's static
constructor adding itself to a global list of tests. When linking against a
static library, static constructors in an object file will be ignored unless at
least one entity in that object file is linked.

Pigweed's ``pw_unit_test`` implementation provides the
:c:macro:`PW_UNIT_TEST_LINK_FILE_CONTAINING_TEST` macro to support running tests
in a static library.

.. c:macro:: PW_UNIT_TEST_LINK_FILE_CONTAINING_TEST(test_suite_name, test_name)
Ensures tests in a static library are linked and executed. Provide the test
suite name and test name for one test in the file linked into a static
library. Any test in the file may be used, but it is recommended to use the
first for consistency. The test must be in a static library that is a
dependency of this target. Referring to a test that does not exist causes a
linker error.

.. _running-tests:

-------------
Expand Down
8 changes: 5 additions & 3 deletions pw_unit_test/public/pw_unit_test/internal/framework.h
Original file line number Diff line number Diff line change
Expand Up @@ -496,16 +496,18 @@ inline void SetTestSuitesToRun(std::span<std::string_view> test_suites) {
class class_name final : public parent_class { \
private: \
void PigweedTestBody() override; \
\
static ::pw::unit_test::internal::TestInfo test_info_; \
}; \
\
::pw::unit_test::internal::TestInfo class_name::test_info_( \
extern "C" { \
\
::pw::unit_test::internal::TestInfo _pw_unit_test_Info_##suite##_##name( \
#suite, \
#name, \
__FILE__, \
::pw::unit_test::internal::Framework::CreateAndRunTest<class_name>); \
\
} /* extern "C" */ \
\
void class_name::PigweedTestBody()

#define _PW_TEST_ASSERT(expectation) \
Expand Down
57 changes: 57 additions & 0 deletions pw_unit_test/public/pw_unit_test/static_library_support.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once

#include "gtest/gtest.h"

// Ensures tests in a static library are linked and executed. Provide the test
// suite name and test name for one test in the file linked into a static
// library. Any test in the file may be used, but it is recommended to use the
// first for consistency. The test must be in a static library that is a
// dependency of this target. Referring to a test that does not exist causes a
// linker error.
//
// The linker usually ignores tests linked through a static library. This is
// because test registration relies on the test instance's static constructor
// adding itself to a global list of tests. When linking against a static
// library, static constructors in an object file will be ignored unless at
// least one entity in that object file is linked.
//
// This macro works by passing the internal TestInfo instance to a constructor
// defined in a source file. This guarantees that the TestInfo instance is
// referenced, so the linker will link it and the other tests in that file.
#define PW_UNIT_TEST_LINK_FILE_CONTAINING_TEST(suite, name) \
_PW_UNIT_TEST_LINK_TESTS(_pw_unit_test_Info_##suite##_##name)

#define _PW_UNIT_TEST_LINK_TESTS(info) \
extern "C" { \
\
extern ::pw::unit_test::internal::TestInfo info; \
\
[[maybe_unused]] const ::pw::unit_test::internal::ReferToTestInfo \
_pw_unit_test_reference_to_ensure_link_##info(info); \
\
} /* extern "C" */ \
\
static_assert(true, "Macros must end with a semicolon")

namespace pw::unit_test::internal {

// Refers to the TestInfo to ensure it is linked in.
class ReferToTestInfo {
public:
explicit ReferToTestInfo(const TestInfo& info);
};

} // namespace pw::unit_test::internal
29 changes: 29 additions & 0 deletions pw_unit_test/static_library_archived_tests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

#include "gtest/gtest.h"

namespace pw::unit_test {

extern int test_1_executions;
extern int test_2_executions;

namespace {

TEST(StaticLibraryArchivedTest, Test1) { test_1_executions += 1; }

TEST(StaticLibraryArchivedTest, Test2) { test_2_executions += 1; }

} // namespace
} // namespace pw::unit_test
35 changes: 35 additions & 0 deletions pw_unit_test/static_library_missing_archived_tests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

#include "gtest/gtest.h"

namespace pw::unit_test {

extern int test_3_executions_not_expected;
extern int test_4_executions_not_expected;

namespace {

TEST(StaticLibraryArchivedTest, ShouldNotRunTest3) {
test_3_executions_not_expected += 1;
}

TEST(StaticLibraryArchivedTest, ShouldNotRunTest4) {
test_4_executions_not_expected += 1;
}

TEST(StaticLibraryArchivedTest, Fails) { FAIL(); }

} // namespace
} // namespace pw::unit_test
23 changes: 23 additions & 0 deletions pw_unit_test/static_library_support.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

#include "pw_unit_test/static_library_support.h"

namespace pw::unit_test::internal {

ReferToTestInfo::ReferToTestInfo(const TestInfo& info) {
[[maybe_unused]] const TestInfo* volatile force_reference = &info;
}

} // namespace pw::unit_test::internal
Loading

0 comments on commit fdc2388

Please sign in to comment.