|
| 1 | +"""Streamlit testing for Basic JESD204 Calculator page.""" |
| 2 | + |
| 3 | +import pathlib |
| 4 | +import sys |
| 5 | +import time |
| 6 | + |
| 7 | +from streamlit.testing.v1 import AppTest |
| 8 | + |
| 9 | +app_path = ( |
| 10 | + pathlib.Path(__file__).parent.parent.parent |
| 11 | + / "adijif" |
| 12 | + / "tools" |
| 13 | + / "explorer" |
| 14 | +) |
| 15 | +app_file_path = app_path / "main.py" |
| 16 | + |
| 17 | +sys.path.append(str(app_path)) |
| 18 | + |
| 19 | + |
| 20 | +def navigate_to_jesdbasic(at: AppTest) -> AppTest: |
| 21 | + """Navigate the sidebar to the Basic JESD204 Calculator page.""" |
| 22 | + sb = at.sidebar |
| 23 | + for item in sb.radio: |
| 24 | + if item.label == "Select a Tool": |
| 25 | + item.set_value("Basic JESD204 Calculator").run() |
| 26 | + break |
| 27 | + time.sleep(0.5) |
| 28 | + return at |
| 29 | + |
| 30 | + |
| 31 | +def test_jesdbasic_page_loads() -> None: |
| 32 | + """Test that the Basic JESD204 Calculator page loads without errors.""" |
| 33 | + at = AppTest.from_file(app_file_path).run() |
| 34 | + navigate_to_jesdbasic(at) |
| 35 | + |
| 36 | + assert not at.exception |
| 37 | + assert len(at.title) > 0 |
| 38 | + assert at.title[0].value == "Basic JESD204 Calculator" |
| 39 | + |
| 40 | + |
| 41 | +def test_jesdbasic_default_inputs_present() -> None: |
| 42 | + """Test that default input widgets are present with expected defaults.""" |
| 43 | + at = AppTest.from_file(app_file_path).run() |
| 44 | + navigate_to_jesdbasic(at) |
| 45 | + |
| 46 | + labels = {ni.label: ni for ni in at.number_input} |
| 47 | + assert "L (number of lanes)" in labels, "L input not found" |
| 48 | + assert "M (number of converters)" in labels, "M input not found" |
| 49 | + assert "Np (Bits per sample)" in labels, "Np input not found" |
| 50 | + |
| 51 | + assert labels["L (number of lanes)"].value == 4 |
| 52 | + assert labels["M (number of converters)"].value == 4 |
| 53 | + assert labels["Np (Bits per sample)"].value == 16 |
| 54 | + |
| 55 | + assert not at.exception |
| 56 | + |
| 57 | + |
| 58 | +def test_jesdbasic_jesd_class_selector() -> None: |
| 59 | + """Test that the JESD204 Class selector is present with correct options.""" |
| 60 | + at = AppTest.from_file(app_file_path).run() |
| 61 | + navigate_to_jesdbasic(at) |
| 62 | + |
| 63 | + class_sel = None |
| 64 | + for sb in at.selectbox: |
| 65 | + if sb.label == "JESD204 Class": |
| 66 | + class_sel = sb |
| 67 | + break |
| 68 | + |
| 69 | + assert class_sel is not None, "JESD204 Class selectbox not found" |
| 70 | + assert "JESD204B" in class_sel.options |
| 71 | + assert "JESD204C" in class_sel.options |
| 72 | + assert class_sel.value == "JESD204B" |
| 73 | + |
| 74 | + assert not at.exception |
| 75 | + |
| 76 | + |
| 77 | +def test_jesdbasic_clock_ref_sample_rate_mode() -> None: |
| 78 | + """Test that Sample Rate mode shows the Sample Rate input.""" |
| 79 | + at = AppTest.from_file(app_file_path).run() |
| 80 | + navigate_to_jesdbasic(at) |
| 81 | + |
| 82 | + # Default mode is Sample Rate — verify the sample rate input is visible |
| 83 | + labels = [ni.label for ni in at.number_input] |
| 84 | + assert any("Sample Rate" in lbl for lbl in labels), ( |
| 85 | + "Sample Rate input not found in default mode" |
| 86 | + ) |
| 87 | + |
| 88 | + assert not at.exception |
| 89 | + |
| 90 | + |
| 91 | +def test_jesdbasic_clock_ref_lane_rate_mode() -> None: |
| 92 | + """Test that switching to Lane Rate mode shows the Lane Rate input.""" |
| 93 | + at = AppTest.from_file(app_file_path).run() |
| 94 | + navigate_to_jesdbasic(at) |
| 95 | + |
| 96 | + for sb in at.selectbox: |
| 97 | + if sb.label == "Clock Reference Source": |
| 98 | + sb.set_value("Lane Rate").run() |
| 99 | + break |
| 100 | + |
| 101 | + labels = [ni.label for ni in at.number_input] |
| 102 | + assert any("Lane Rate" in lbl for lbl in labels), ( |
| 103 | + "Lane Rate input not found after switching Clock Reference Source" |
| 104 | + ) |
| 105 | + |
| 106 | + assert not at.exception |
| 107 | + |
| 108 | + |
| 109 | +def test_jesdbasic_output_table_present() -> None: |
| 110 | + """Test that the Derived Parameters table is rendered.""" |
| 111 | + at = AppTest.from_file(app_file_path).run() |
| 112 | + navigate_to_jesdbasic(at) |
| 113 | + |
| 114 | + assert len(at.table) > 0, "Output table not found" |
| 115 | + |
| 116 | + # Table should contain Lane Rate and Core Clock rows |
| 117 | + table_df = at.table[0].value |
| 118 | + params = list(table_df["Parameter"]) |
| 119 | + assert "Lane Rate (Gbps)" in params, ( |
| 120 | + "Lane Rate row missing from output table" |
| 121 | + ) |
| 122 | + assert "Core Clock (MHz)" in params, ( |
| 123 | + "Core Clock row missing from output table" |
| 124 | + ) |
| 125 | + |
| 126 | + assert not at.exception |
| 127 | + |
| 128 | + |
| 129 | +def test_jesdbasic_lane_rate_calculation_sample_rate_mode() -> None: |
| 130 | + """Test lane rate calculation in Sample Rate mode with known values.""" |
| 131 | + at = AppTest.from_file(app_file_path).run() |
| 132 | + navigate_to_jesdbasic(at) |
| 133 | + |
| 134 | + # Set known values: L=4, M=4, Np=16, Sample Rate=1e8 SPS, JESD204B |
| 135 | + # Expected lane rate = (4 * 16 * 1e8 * 10/8) / 4 / 1e9 = 2.0 Gbps |
| 136 | + for ni in at.number_input: |
| 137 | + if ni.label == "Sample Rate (SPS)": |
| 138 | + ni.set_value(1e8).run() |
| 139 | + break |
| 140 | + |
| 141 | + assert not at.exception |
| 142 | + table_df = at.table[0].value |
| 143 | + row = table_df[table_df["Parameter"] == "Lane Rate (Gbps)"] |
| 144 | + assert len(row) == 1 |
| 145 | + assert abs(float(row["Value"].iloc[0]) - 2.0) < 1e-6 |
| 146 | + |
| 147 | + |
| 148 | +def test_jesdbasic_sample_rate_calculation_lane_rate_mode() -> None: |
| 149 | + """Test sample rate calculation in Lane Rate mode with known values.""" |
| 150 | + at = AppTest.from_file(app_file_path).run() |
| 151 | + navigate_to_jesdbasic(at) |
| 152 | + |
| 153 | + for sb in at.selectbox: |
| 154 | + if sb.label == "Clock Reference Source": |
| 155 | + sb.set_value("Lane Rate").run() |
| 156 | + break |
| 157 | + |
| 158 | + # Set lane rate to 10 Gbps with defaults L=4, M=4, Np=16, JESD204B |
| 159 | + # Expected sample rate = (10e9 * 4) / (4 * 16 * 10/8) / 1e6 = 500 MSPS |
| 160 | + for ni in at.number_input: |
| 161 | + if ni.label == "Lane Rate (Gbps)": |
| 162 | + ni.set_value(10.0).run() |
| 163 | + break |
| 164 | + |
| 165 | + assert not at.exception |
| 166 | + table_df = at.table[0].value |
| 167 | + row = table_df[table_df["Parameter"] == "Sample Rate (MSPS)"] |
| 168 | + assert len(row) == 1 |
| 169 | + assert abs(float(row["Value"].iloc[0]) - 500.0) < 1e-6 |
| 170 | + |
| 171 | + |
| 172 | +def test_jesdbasic_jesd204c_encoding() -> None: |
| 173 | + """Test that switching to JESD204C uses 66/64 encoding factor.""" |
| 174 | + at = AppTest.from_file(app_file_path).run() |
| 175 | + navigate_to_jesdbasic(at) |
| 176 | + |
| 177 | + for sb in at.selectbox: |
| 178 | + if sb.label == "JESD204 Class": |
| 179 | + sb.set_value("JESD204C").run() |
| 180 | + break |
| 181 | + |
| 182 | + assert not at.exception |
| 183 | + |
| 184 | + # With L=4, M=4, Np=16, SR=1e8, JESD204C (66/64 encoding): |
| 185 | + # lane rate = (4 * 16 * 1e8 * 66/64) / 4 / 1e9 = 1.65625 Gbps |
| 186 | + for ni in at.number_input: |
| 187 | + if ni.label == "Sample Rate (SPS)": |
| 188 | + ni.set_value(1e8).run() |
| 189 | + break |
| 190 | + |
| 191 | + assert not at.exception |
| 192 | + table_df = at.table[0].value |
| 193 | + row = table_df[table_df["Parameter"] == "Lane Rate (Gbps)"] |
| 194 | + assert len(row) == 1 |
| 195 | + expected = (4 * 16 * 1e8 * 66 / 64) / 4 / 1e9 |
| 196 | + assert abs(float(row["Value"].iloc[0]) - expected) < 1e-6 |
| 197 | + |
| 198 | + |
| 199 | +def test_jesdbasic_core_clock_jesd204b() -> None: |
| 200 | + """Test core clock calculation for JESD204B.""" |
| 201 | + at = AppTest.from_file(app_file_path).run() |
| 202 | + navigate_to_jesdbasic(at) |
| 203 | + |
| 204 | + for ni in at.number_input: |
| 205 | + if ni.label == "Sample Rate (SPS)": |
| 206 | + ni.set_value(1e8).run() |
| 207 | + break |
| 208 | + |
| 209 | + assert not at.exception |
| 210 | + |
| 211 | + # With lane rate = 2.0 Gbps, JESD204B core clock = 2.0 / 40 * 1e3 = 50 MHz |
| 212 | + table_df = at.table[0].value |
| 213 | + row = table_df[table_df["Parameter"] == "Core Clock (MHz)"] |
| 214 | + assert len(row) == 1 |
| 215 | + assert abs(float(row["Value"].iloc[0]) - 50.0) < 1e-6 |
| 216 | + |
| 217 | + |
| 218 | +def test_jesdbasic_no_exception_on_param_changes() -> None: |
| 219 | + """Test that changing L, M, Np inputs produces no exceptions.""" |
| 220 | + at = AppTest.from_file(app_file_path).run() |
| 221 | + navigate_to_jesdbasic(at) |
| 222 | + |
| 223 | + for label, value in [ |
| 224 | + ("L (number of lanes)", 8), |
| 225 | + ("M (number of converters)", 2), |
| 226 | + ("Np (Bits per sample)", 12), |
| 227 | + ]: |
| 228 | + for ni in at.number_input: |
| 229 | + if ni.label == label: |
| 230 | + ni.set_value(value).run() |
| 231 | + assert not at.exception, ( |
| 232 | + f"Exception when setting {label}={value}" |
| 233 | + ) |
| 234 | + break |
0 commit comments