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