Coverage for tests/unit_tests/scons/testing.py: 100%
35 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03:53 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03:53 +0000
1"""
2Helpers for apio's scons testing."""
4import SCons.Script.SConsOptions
5import SCons.Node.FS
6import SCons.Environment
7import SCons.Script.Main
8from google.protobuf import text_format
9from apio.scons.apio_env import ApioEnv
10from apio.common.proto.apio_scons_pb2 import (
11 SconsParams,
12 TargetParams,
13 ApioEnvParams,
14)
17TEST_PARAMS = """
18timestamp: "20123412052"
19arch: ice40
20fpga_info {
21 fpga_id: "ice40hx4k-tq144-8k"
22 part_num: "ICE40HX4K-TQ144"
23 size: "8k"
24 ice40_params {
25 type: "hx8k"
26 package: "tq144:4k"
27 }
28}
29verbosity {
30 all: false
31 synth: false
32 pnr: false
33}
34environment {
35 platform_id: "darwin-arm64"
36 yosys_path: "/Users/user/.apio/packages/oss-cad-suite/share/yosys"
37 trellis_path: "/Users/user/.apio/packages/oss-cad-suite/share/trellis"
38}
39apio_env_params {
40 env_name: "default"
41 board_id: "alhambra-ii"
42 top_module: "main"
43}
44"""
47class SconsHacks:
48 """A collection of static methods that encapsulate scons access outside of
49 the official scons API. Hopefully this will not be too difficult to adapt
50 in future versions of SCons."""
52 @staticmethod
53 def reset_scons_state() -> None:
54 """Reset the relevant SCons global variables. Unfortunately scons
55 uses a few global variables to hold its state. This works well in
56 normal operation where an scons process contains a single scons
57 session but with pytest testing, where multiple independent tests
58 are running in the same process, we need to reset though variables
59 before each test."""
61 # -- The Cons.Script.Main.OptionsParser variables contains the command
62 # -- line options of scons. We reset them here and tests can access
63 # -- them using SetOption() and GetOption().
65 parser = SCons.Script.SConsOptions.Parser("my_fake_version")
66 values = SCons.Script.SConsOptions.SConsValues(
67 parser.get_default_values()
68 )
69 parser.parse_args(args=[], values=values)
70 SCons.Script.Main.OptionsParser = parser
72 # -- Reset the scons target list variable.
73 SCons.Node.FS.default_fs = None
75 # -- Clear the SCons targets
76 SCons.Environment.CleanTargets = {}
79def make_test_scons_params() -> SconsParams:
80 """Create a fake scons params for testing."""
81 return text_format.Parse(TEST_PARAMS, SconsParams())
84def make_test_apio_env(
85 *,
86 targets: list[str] | None = None,
87 platform_id: str | None = None,
88 is_windows: bool | None = None,
89 # debug_level: int = 0,
90 apio_env_params: ApioEnvParams | None = None,
91 target_params: TargetParams | None = None,
92) -> ApioEnv:
93 """Creates a fresh apio env for testing. The env is created
94 with the current directory as the root dir.
95 """
97 # -- Specify both or nether.
98 assert (platform_id is None) == (is_windows is None)
100 # -- Bring scons to a starting state.
101 SconsHacks.reset_scons_state()
103 # -- Create default params.
104 scons_params = make_test_scons_params()
106 # -- Set debug level
107 # scons_params.environment.debug_level = debug_level
109 # -- Apply user overrides.
110 if platform_id is not None:
111 scons_params.environment.platform_id = platform_id
112 if is_windows is not None:
113 scons_params.environment.is_windows = is_windows
114 if apio_env_params is not None:
115 scons_params.apio_env_params.MergeFrom(apio_env_params)
116 if target_params is not None:
117 scons_params.target.MergeFrom(target_params)
119 # -- Determine scons target.
120 if targets is not None:
121 command_line_targets = targets
122 else:
123 command_line_targets = ["build"]
125 # -- Create and return the apio env.
126 return ApioEnv(
127 command_line_targets=command_line_targets, scons_params=scons_params
128 )