Skip to content

Commit 1aed3f7

Browse files
feasel0restyled-commits
authored andcommitted
Generating separate binaries for each unit test for EFR32 (project-chip#35028)
* silabs full changes, flashable just if * silabs full changes, flashable just if (corrected) * silabs changes, flashable changes * yes executables.gni, cts.gni, args.gni, test_driver build.gn * yes executables.gni, args.gni, test_driver build.gn * Modified chip_test_suite to handle logic for both efr32 test_driver and chip_link_test * Doc update * Added final newline * Comment updates * Remove deprecated `tests` variable for per-test custom mains. * switched to shutil.copy instead of subprocess copy * Added chip_link_tests to test_driver/efr32/args.gni and removed special logic from chip_test_suite * Restyled by gn * Restyled by autopep8 * Punctuation change * Added special exception for darwin to always include test_sources in common lib. * Added comment re darwin exception * Restyled by gn * Revisions to builder scripts - removing map() usage and propagating OSError to make_wrapper. * Restyled by autopep8 --------- Co-authored-by: Restyled.io <commits@restyled.io>
1 parent bdec4b0 commit 1aed3f7

File tree

17 files changed

+265
-378
lines changed

17 files changed

+265
-378
lines changed

.github/.wordlist.txt

-2
Original file line numberDiff line numberDiff line change
@@ -950,8 +950,6 @@ NitricOxideConcentrationMeasurement
950950
NitrogenDioxideConcentrationMeasurement
951951
nl
952952
nltest
953-
NLUnitTest
954-
NLUnitTests
955953
nmcli
956954
nmtui
957955
noc

build/chip/chip_test.gni

-71
This file was deleted.

build/chip/chip_test_suite.gni

+42-59
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@
1414

1515
import("//build_overrides/build.gni")
1616
import("//build_overrides/chip.gni")
17+
import("//build_overrides/pigweed.gni")
1718

18-
import("${chip_root}/build/chip/chip_test.gni")
1919
import("${chip_root}/build/chip/tests.gni")
2020
import("${dir_pw_unit_test}/test.gni")
2121

2222
assert(chip_build_tests)
2323

24+
declare_args() {
25+
# These may be overridden in args.gni to build platform-specific test binaries.
26+
test_executable_output_name = ""
27+
test_executable_output_name_suffix = ""
28+
test_executable_ldflags = []
29+
}
30+
2431
# Define CHIP unit tests
2532
#
2633
# Simple usage
@@ -41,50 +48,34 @@ assert(chip_build_tests)
4148
# "${chip_root}/src/lib/foo", # add dependencies here
4249
# ]
4350
# }
44-
#
45-
#
46-
# Deprecated usage (writing own driver files):
47-
#
48-
# chip_test_suite("tests") {
49-
# output_name = "libFooTests"
50-
#
51-
# sources = [
52-
# "TestDeclarations.h",
53-
# "TestFoo.cpp",
54-
# "TestBar.cpp",
55-
# ]
56-
#
57-
# public_deps = [
58-
# "${chip_root}/src/lib/foo", # add dependencies here
59-
# ]
60-
#
61-
# tests = [
62-
# "TestFoo", # Assumes TestFooDriver.cpp exists
63-
# "TestBar", # Assumes TestBarDriver.cpp exists
64-
# ]
65-
# }
6651

6752
#
6853
template("chip_test_suite") {
6954
_suite_name = target_name
7055

71-
# Ensures that the common library has sources containing both common
72-
# and individual unit tests.
73-
if (!defined(invoker.sources)) {
74-
invoker.sources = []
75-
}
76-
77-
if (defined(invoker.test_sources)) {
78-
invoker.sources += invoker.test_sources
56+
exclude_variables = [ "tests" ]
57+
if (chip_link_tests && chip_device_platform != "darwin") {
58+
# Common library shouldn't have all the individual unit tests, only the common sources.
59+
exclude_variables += [ "test_sources" ]
60+
# NOTE: For `Build on Darwin (clang, python_lib, simulated)` the test_sources must be in common lib.
61+
} else {
62+
# Common library should have all the individual unit tests, in addition to the common sources.
63+
if (!defined(invoker.sources)) {
64+
invoker.sources = []
65+
}
66+
if (defined(invoker.test_sources)) {
67+
invoker.sources += invoker.test_sources
68+
}
7969
}
8070

71+
# Target for the common library. Contains all the common sources, and sometimes all the individual test sources.
8172
if (chip_build_test_static_libraries) {
8273
_target_type = "static_library"
8374
} else {
8475
_target_type = "source_set"
8576
}
8677
target(_target_type, "${_suite_name}.lib") {
87-
forward_variables_from(invoker, "*", [ "tests" ])
78+
forward_variables_from(invoker, "*", exclude_variables)
8879

8980
output_dir = "${root_out_dir}/lib"
9081

@@ -102,6 +93,8 @@ template("chip_test_suite") {
10293
public_deps += [ "${chip_root}/src/platform/logging:default" ]
10394
}
10495
}
96+
97+
# Build a source_set or a flashable executable for each individual unit test source, which also includes the common files.
10598
if (chip_link_tests) {
10699
tests = []
107100

@@ -115,50 +108,38 @@ template("chip_test_suite") {
115108
}
116109

117110
pw_test(_test_name) {
111+
# Forward certain variables from the invoker.
118112
forward_variables_from(invoker,
119113
[
120114
"deps",
121115
"public_deps",
122116
"cflags",
123117
"configs",
124118
])
119+
120+
# Link to the common lib for this suite so we get its `sources`.
125121
public_deps += [ ":${_suite_name}.lib" ]
126-
sources = [ _test ]
127-
output_dir = _test_output_dir
128-
}
129-
tests += [ _test_name ]
130-
}
131-
}
132122

133-
if (defined(invoker.tests)) {
134-
foreach(_test, invoker.tests) {
135-
_test_output_dir = "${root_out_dir}/tests"
136-
if (defined(invoker.output_dir)) {
137-
_test_output_dir = invoker.output_dir
138-
}
123+
# Set variables that the platform executable may need.
124+
if (test_executable_output_name != "") {
125+
output_name = test_executable_output_name + _test_name +
126+
test_executable_output_name_suffix
127+
}
128+
ldflags = test_executable_ldflags
129+
130+
# Add the individual test source file (e.g. "TestSomething.cpp").
131+
sources = [ _test ]
139132

140-
pw_test(_test) {
141-
forward_variables_from(invoker,
142-
[
143-
"deps",
144-
"public_deps",
145-
"cflags",
146-
"configs",
147-
])
148-
public_deps += [ ":${_suite_name}.lib" ]
149-
test_main = ""
150-
sources = [
151-
"${_test}.cpp",
152-
"${_test}Driver.cpp",
153-
]
154133
output_dir = _test_output_dir
155134
}
156-
tests += [ _test ]
135+
tests += [ _test_name ]
157136
}
158137
}
159138

160139
group(_suite_name) {
161140
deps = []
141+
142+
# Add each individual unit test.
162143
foreach(_test, tests) {
163144
deps += [ ":${_test}" ]
164145
}
@@ -167,6 +148,8 @@ template("chip_test_suite") {
167148
if (chip_pw_run_tests) {
168149
group("${_suite_name}_run") {
169150
deps = []
151+
152+
# Add the .run targets created by pw_test.
170153
foreach(_test, tests) {
171154
deps += [ ":${_test}.run" ]
172155
}

build/toolchain/flashable_executable.gni

+13-5
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ template("gen_flashing_script") {
8686
template("flashable_executable") {
8787
executable_target = "$target_name.executable"
8888

89+
if (!defined(invoker.output_dir)) {
90+
invoker.output_dir = root_out_dir
91+
}
92+
8993
if (defined(invoker.flashing_script_name)) {
9094
# Generating the flashing script is the final target.
9195
final_target = "$target_name.flashing"
@@ -110,7 +114,10 @@ template("flashable_executable") {
110114
data_deps += invoker.data_deps
111115
}
112116

113-
write_runtime_deps = "${root_out_dir}/${flashbundle_name}"
117+
# Invoker can stop this template from creating the flashbundle.txt by setting flashbundle_name to empty string.
118+
if (flashbundle_name != "") {
119+
write_runtime_deps = "${invoker.output_dir}/${flashbundle_name}"
120+
}
114121
}
115122

116123
if (defined(invoker.objcopy_image_name)) {
@@ -124,8 +131,8 @@ template("flashable_executable") {
124131
objcopy = invoker.objcopy
125132

126133
objcopy_convert(image_target) {
127-
conversion_input = "${root_out_dir}/${invoker.output_name}"
128-
conversion_output = "${root_out_dir}/${image_name}"
134+
conversion_input = "${invoker.output_dir}/${invoker.output_name}"
135+
conversion_output = "${invoker.output_dir}/${image_name}"
129136
conversion_target_format = image_format
130137
deps = [ ":$executable_target" ]
131138
}
@@ -141,7 +148,8 @@ template("flashable_executable") {
141148
gen_flashing_script("$target_name.flashing") {
142149
flashing_script_generator = invoker.flashing_script_generator
143150
flashing_script_inputs = invoker.flashing_script_inputs
144-
flashing_script_name = "$root_out_dir/${invoker.flashing_script_name}"
151+
flashing_script_name =
152+
"${invoker.output_dir}/${invoker.flashing_script_name}"
145153
if (defined(invoker.flashing_options)) {
146154
flashing_options = invoker.flashing_options
147155
} else {
@@ -155,7 +163,7 @@ template("flashable_executable") {
155163

156164
flashing_options += [
157165
"--application",
158-
rebase_path(image_name, root_out_dir, root_out_dir),
166+
rebase_path(image_name, invoker.output_dir, invoker.output_dir),
159167
]
160168
data_deps = [ ":$image_target" ]
161169
}

scripts/build/builders/efr32.py

+47-8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import glob
16+
import logging
1517
import os
1618
import shlex
1719
import subprocess
@@ -78,7 +80,7 @@ def FlashBundleName(self):
7880
elif self == Efr32App.PUMP:
7981
return 'pump_app.flashbundle.txt'
8082
elif self == Efr32App.UNIT_TEST:
81-
return 'efr32_device_tests.flashbundle.txt'
83+
return os.path.join('tests', 'efr32_device_tests.flashbundle.txt')
8284
else:
8385
raise Exception('Unknown app type: %r' % self)
8486

@@ -259,27 +261,64 @@ def __init__(self,
259261
def GnBuildArgs(self):
260262
return self.extra_gn_options
261263

264+
def _bundle(self):
265+
# Only unit-test needs to generate the flashbundle here. All other examples will generate a flashbundle via the silabs_executable template.
266+
if self.app == Efr32App.UNIT_TEST:
267+
flash_bundle_path = os.path.join(self.output_dir, self.app.FlashBundleName())
268+
logging.info(f'Generating flashbundle {flash_bundle_path}')
269+
270+
patterns = [
271+
os.path.join(self.output_dir, "tests", "*.flash.py"),
272+
os.path.join(self.output_dir, "tests", "*.s37"),
273+
os.path.join(self.output_dir, "tests", "silabs_firmware_utils.py"),
274+
os.path.join(self.output_dir, "tests", "firmware_utils.py"),
275+
]
276+
277+
# Generate the list of files by globbing each pattern.
278+
files = []
279+
for pattern in patterns:
280+
files.extend([os.path.basename(x) for x in glob.glob(pattern)])
281+
282+
# Create the bundle file.
283+
with open(flash_bundle_path, 'w') as bundle_file:
284+
bundle_file.write("\n".join(files))
285+
262286
def build_outputs(self):
263287
extensions = ["out", "hex"]
264288
if self.options.enable_link_map_file:
265289
extensions.append("out.map")
266-
for ext in extensions:
267-
name = f"{self.app.AppNamePrefix()}.{ext}"
268-
yield BuilderOutput(os.path.join(self.output_dir, name), name)
290+
291+
if self.app == Efr32App.UNIT_TEST:
292+
# Efr32 unit-test generates the "tests" subdir with a set of files for each individual unit test source.
293+
for ext in extensions:
294+
pattern = os.path.join(self.output_dir, "tests", f"*.{ext}")
295+
for name in [os.path.basename(x) for x in glob.glob(pattern)]:
296+
yield BuilderOutput(os.path.join(self.output_dir, "tests", name), name)
297+
else:
298+
# All other examples have just one set of files.
299+
for ext in extensions:
300+
name = f"{self.app.AppNamePrefix()}.{ext}"
301+
yield BuilderOutput(os.path.join(self.output_dir, name), name)
269302

270303
if self.app == Efr32App.UNIT_TEST:
271304
# Include test runner python wheels
272-
for root, dirs, files in os.walk(os.path.join(self.output_dir, 'chip_nl_test_runner_wheels')):
305+
for root, dirs, files in os.walk(os.path.join(self.output_dir, 'chip_pw_test_runner_wheels')):
273306
for file in files:
274307
yield BuilderOutput(
275308
os.path.join(root, file),
276-
os.path.join("chip_nl_test_runner_wheels", file))
309+
os.path.join("chip_pw_test_runner_wheels", file))
277310

278-
# Figure out flash bundle files and build accordingly
311+
def bundle_outputs(self):
312+
# If flashbundle creation is enabled, the outputs will include the s37 and flash.py files, plus the two firmware utils scripts that support flash.py.
313+
# For the unit-test example, there will be a s37 and flash.py file for each unit test source.
279314
with open(os.path.join(self.output_dir, self.app.FlashBundleName())) as f:
280315
for name in filter(None, [x.strip() for x in f.readlines()]):
316+
if self.app == Efr32App.UNIT_TEST:
317+
sourcepath = os.path.join(self.output_dir, "tests", name) # Unit tests are in the "tests" subdir.
318+
else:
319+
sourcepath = os.path.join(self.output_dir, name)
281320
yield BuilderOutput(
282-
os.path.join(self.output_dir, name),
321+
sourcepath,
283322
os.path.join("flashbundle", name))
284323

285324
def generate(self):

0 commit comments

Comments
 (0)