makcu 2.1.2__py3-none-any.whl → 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
makcu/__main__.py CHANGED
@@ -1,388 +1,389 @@
1
- import sys
2
- import os
3
- from pathlib import Path
4
- from typing import List, NoReturn, Optional, Tuple
5
- import pytest
6
- import time
7
- from makcu import create_controller, MakcuConnectionError, MakcuController
8
- import json
9
- import re
10
- import subprocess
11
-
12
- makcu_version = "v2.1.2"
13
-
14
- def debug_console():
15
- controller = create_controller()
16
- transport = controller.transport
17
-
18
- print("🔧 Makcu Debug Console")
19
- print("Type a raw command (e.g., km.version()) and press Enter.")
20
- print("Type 'exit' or 'quit' to leave.")
21
-
22
- command_counter = 0
23
-
24
- while True:
25
- try:
26
- cmd = input(">>> ").strip()
27
- if cmd.lower() in {"exit", "quit"}:
28
- break
29
- if not cmd:
30
- continue
31
-
32
- command_counter += 1
33
-
34
- response = transport.send_command(cmd, expect_response=True)
35
-
36
- if response and response.strip():
37
- if response.strip() == cmd:
38
- print(f"{cmd}")
39
- else:
40
- print(f"{response}")
41
- else:
42
- print("(no response)")
43
-
44
- except Exception as e:
45
- print(f"⚠️ Error: {e}")
46
-
47
- controller.disconnect()
48
- print("Disconnected.")
49
-
50
- def test_port(port: str) -> None:
51
- try:
52
- print(f"Trying to connect to {port}...")
53
- makcu = MakcuController(fallback_com_port=port, send_init=False, override_port=True)
54
- makcu.connect()
55
- if makcu.is_connected:
56
- print(f"✅ Successfully connected to {port}.")
57
- makcu.disconnect()
58
- except MakcuConnectionError as e:
59
- if "FileNotFoundError" in str(e):
60
- print(f" Port {port} does not exist. Please check the port name.")
61
- else:
62
- print(f"❌ Failed to connect to {port}: ")
63
- except Exception as e:
64
- print(f"❌ Unexpected error: {e}")
65
-
66
- def check_pytest_html_installed() -> bool:
67
- """Check if pytest-html is installed."""
68
- try:
69
- import pytest_html
70
- return True
71
- except ImportError:
72
- return False
73
-
74
- def find_writable_directory() -> Path:
75
- """Find a writable directory for the HTML report."""
76
- # Try current working directory first
77
- cwd = Path.cwd()
78
- if os.access(cwd, os.W_OK):
79
- return cwd
80
-
81
- # Try user's home directory
82
- home = Path.home()
83
- if os.access(home, os.W_OK):
84
- return home
85
-
86
- # Try temp directory as last resort
87
- import tempfile
88
- return Path(tempfile.gettempdir())
89
-
90
- def parse_html_results(html_file: Path) -> Tuple[List[Tuple[str, str, int]], int]:
91
- if not html_file.exists():
92
- raise FileNotFoundError(f"HTML report not found: {html_file}")
93
-
94
- with open(html_file, 'r', encoding='utf-8') as f:
95
- content = f.read()
96
-
97
- match = re.search(r'data-jsonblob="([^"]*)"', content)
98
- if not match:
99
- raise ValueError("Could not find JSON data in HTML report")
100
-
101
- json_str = match.group(1)
102
- json_str = json_str.replace('"', '"').replace(''', "'").replace('&', '&')
103
-
104
- try:
105
- data = json.loads(json_str)
106
- except json.JSONDecodeError as e:
107
- raise ValueError(f"Failed to parse JSON data: {e}")
108
-
109
- test_results = []
110
- total_ms = 0
111
-
112
- skip_tests = {'test_connect_to_port'}
113
-
114
- for test_id, test_data_list in data.get('tests', {}).items():
115
- test_name = test_id.split('::')[-1]
116
- if test_name in skip_tests:
117
- continue
118
-
119
- for test_data in test_data_list:
120
- status = test_data.get('result', 'UNKNOWN')
121
- duration_str = test_data.get('duration', '0 ms')
122
-
123
- duration_match = re.search(r'(\d+)\s*ms', duration_str)
124
- duration_ms = int(duration_match.group(1)) if duration_match else 0
125
- total_ms += duration_ms
126
-
127
- test_results.append((test_name, status, duration_ms))
128
-
129
- return test_results, total_ms
130
-
131
- def run_tests() -> NoReturn:
132
- # Check if pytest-html is installed
133
- if not check_pytest_html_installed():
134
- print("❌ pytest-html is not installed. Please install it via:")
135
- print(" pip install pytest-html")
136
- sys.exit(1)
137
-
138
- try:
139
- from rich.console import Console
140
- from rich.table import Table
141
- from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
142
- from rich.panel import Panel
143
- from rich.align import Align
144
- from rich import print as rprint
145
- from rich.text import Text
146
-
147
- console = Console()
148
-
149
- header = Panel.fit(
150
- f"[bold cyan]Makcu Test Suite {makcu_version}[/bold cyan]\n[dim]High-Performance Python Library[/dim]",
151
- border_style="bright_blue"
152
- )
153
- console.print(Align.center(header))
154
- console.print()
155
-
156
- package_dir: Path = Path(__file__).resolve().parent
157
- test_file: Path = package_dir / "test_suite.py"
158
-
159
- # Find writable directory and create HTML path
160
- writable_dir = find_writable_directory()
161
- html_file: Path = writable_dir / "latest_pytest.html"
162
-
163
- # Clean up old report if it exists
164
- if html_file.exists():
165
- try:
166
- html_file.unlink()
167
- except Exception:
168
- pass
169
-
170
- console.print(f"[dim]Running pytest to generate: {html_file}[/dim]")
171
- console.print(f"[dim]Working directory: {Path.cwd()}[/dim]")
172
-
173
- start_time = time.time()
174
-
175
- with Progress(
176
- SpinnerColumn(),
177
- TextColumn("[progress.description]{task.description}"),
178
- BarColumn(),
179
- TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
180
- TimeElapsedColumn(),
181
- console=console,
182
- transient=True
183
- ) as progress:
184
- task = progress.add_task("[cyan]Running tests...", total=100)
185
-
186
- # Run pytest with explicit output capturing
187
- result = subprocess.run(
188
- [
189
- sys.executable, "-m", "pytest",
190
- str(test_file),
191
- "--rootdir", str(package_dir),
192
- "-q",
193
- "--tb=no",
194
- "--html", str(html_file),
195
- "--self-contained-html",
196
- "-v" # Add verbose to help debug
197
- ],
198
- capture_output=True,
199
- text=True
200
- )
201
-
202
- progress.update(task, completed=100)
203
-
204
- # Check if HTML file was created
205
- if not html_file.exists():
206
- console.print(f"[red]❌ HTML report was not created at: {html_file}[/red]")
207
- console.print(f"[yellow]pytest exit code: {result.returncode}[/yellow]")
208
- if result.stdout:
209
- console.print("[yellow]stdout:[/yellow]")
210
- console.print(result.stdout)
211
- if result.stderr:
212
- console.print("[red]stderr:[/red]")
213
- console.print(result.stderr)
214
-
215
- # Try to run tests without HTML report
216
- console.print("\n[yellow]Running tests without HTML report...[/yellow]")
217
- result2 = subprocess.run(
218
- [sys.executable, "-m", "pytest", str(test_file), "-v"],
219
- capture_output=True,
220
- text=True
221
- )
222
- console.print(result2.stdout)
223
- sys.exit(1)
224
-
225
- try:
226
- test_results, total_ms = parse_html_results(html_file)
227
- except (FileNotFoundError, ValueError) as e:
228
- console.print(f"[red]❌ Failed to parse test results: {e}[/red]")
229
- console.print(f"[yellow]⚠️ pytest exit code: {result.returncode}[/yellow]")
230
- sys.exit(1)
231
-
232
- elapsed_time = time.time() - start_time
233
-
234
- table = Table(title="[bold]Test Results[/bold]", show_header=True, header_style="bold magenta")
235
- table.add_column("Test", style="cyan", no_wrap=True)
236
- table.add_column("Status", justify="center")
237
- table.add_column("Time", justify="right", style="yellow")
238
- table.add_column("Performance", justify="center")
239
-
240
- passed = failed = skipped = 0
241
-
242
- for test_name, status, duration_ms in test_results:
243
- display_name = test_name.replace("test_", "").replace("_", " ").title()
244
-
245
- if status.upper() == "PASSED":
246
- status_text = "[green]✅ PASSED[/green]"
247
- passed += 1
248
- elif status.upper() == "FAILED":
249
- status_text = "[red]❌ FAILED[/red]"
250
- failed += 1
251
- elif status.upper() == "SKIPPED":
252
- status_text = "[yellow]⏭️ SKIPPED[/yellow]"
253
- skipped += 1
254
- else:
255
- status_text = status
256
-
257
- time_str = f"{duration_ms}ms" if duration_ms else "-"
258
- if duration_ms <= 3:
259
- perf = "[green]Excellent[/green]"
260
- elif duration_ms <= 5:
261
- perf = "[cyan]Great[/cyan]"
262
- elif duration_ms <= 10:
263
- perf = "[yellow]Good[/yellow]"
264
- elif duration_ms > 0:
265
- perf = "[red]🐌 Needs work[/red]"
266
- else:
267
- perf = "-"
268
-
269
- table.add_row(display_name, status_text, time_str, perf)
270
-
271
- console.print("\n")
272
- console.print(table)
273
- console.print()
274
-
275
- summary = Table.grid(padding=1)
276
- summary.add_column(style="bold cyan", justify="right")
277
- summary.add_column(justify="left")
278
- summary.add_row("Total Tests:", str(len(test_results)))
279
- summary.add_row("Passed:", f"[green]{passed}[/green]")
280
- summary.add_row("Failed:", f"[red]{failed}[/red]" if failed else str(failed))
281
- summary.add_row("Skipped:", f"[yellow]{skipped}[/yellow]" if skipped else str(skipped))
282
- summary.add_row("Total Time:", f"{elapsed_time:.2f}s")
283
- summary.add_row("Avg Time/Test:", f"{total_ms/len(test_results):.1f}ms" if test_results else "0ms")
284
-
285
- console.print(Align.center(Panel(summary, title="[bold]Summary[/bold]", border_style="blue", expand=False)))
286
- console.print()
287
-
288
- if test_results:
289
- avg_time = total_ms / len(test_results)
290
- if avg_time < 3:
291
- perf_text = Text("Performance: ELITE - Ready for 360Hz+ gaming!", style="bold bright_green")
292
- elif avg_time < 5:
293
- perf_text = Text("Performance: EXCELLENT - Ready for 240Hz+ gaming!", style="bold green")
294
- elif avg_time < 10:
295
- perf_text = Text("Performance: GREAT - Ready for 144Hz gaming!", style="bold cyan")
296
- else:
297
- perf_text = Text("Performance: GOOD - Suitable for standard gaming", style="bold yellow")
298
- else:
299
- perf_text = Text("⚠️ No test results parsed. Check your test suite.", style="bold red")
300
-
301
- console.print(Align.center(Panel(perf_text, border_style="green")))
302
-
303
- # Print the location of the HTML report
304
- console.print(f"\n[dim]HTML report saved to: {html_file}[/dim]")
305
-
306
- sys.exit(0 if failed == 0 else 1)
307
-
308
- except ImportError:
309
- print("📦 Rich not installed. Install it via `pip install rich` for enhanced output.")
310
- print("\nFallback to raw pytest output...\n")
311
-
312
- package_dir: Path = Path(__file__).resolve().parent
313
- test_file: Path = package_dir / "test_suite.py"
314
-
315
- # Find writable directory
316
- writable_dir = find_writable_directory()
317
- html_file: Path = writable_dir / "latest_pytest.html"
318
-
319
- print(f"HTML report will be saved to: {html_file}")
320
-
321
- # Use subprocess instead of pytest.main for better control
322
- result = subprocess.run(
323
- [
324
- sys.executable, "-m", "pytest",
325
- str(test_file),
326
- "--rootdir", str(package_dir),
327
- "-q",
328
- "--tb=no",
329
- "--html", str(html_file),
330
- "--self-contained-html"
331
- ],
332
- capture_output=True,
333
- text=True
334
- )
335
-
336
- if not html_file.exists():
337
- print(f"\n❌ HTML report was not created. pytest exit code: {result.returncode}")
338
- if result.stdout:
339
- print("stdout:", result.stdout)
340
- if result.stderr:
341
- print("stderr:", result.stderr)
342
- sys.exit(1)
343
-
344
- try:
345
- test_results, total_ms = parse_html_results(html_file)
346
- passed = sum(1 for _, status, _ in test_results if status.upper() == "PASSED")
347
- failed = sum(1 for _, status, _ in test_results if status.upper() == "FAILED")
348
- skipped = sum(1 for _, status, _ in test_results if status.upper() == "SKIPPED")
349
-
350
- print(f"\n📊 Results: {passed} passed, {failed} failed, {skipped} skipped")
351
- if test_results:
352
- avg_time = total_ms / len(test_results)
353
- print(f"⏱️ Average time per test: {avg_time:.1f}ms")
354
- except (FileNotFoundError, ValueError):
355
- print("\n⚠️ Could not parse HTML results for summary")
356
-
357
- if result.returncode != 0:
358
- print("\n❌ Some tests failed.")
359
- else:
360
- print("\n✅ All tests passed.")
361
-
362
- sys.exit(result.returncode)
363
-
364
- def main() -> None:
365
- args: List[str] = sys.argv[1:]
366
-
367
- if not args:
368
- print("Usage:")
369
- print(" python -m makcu --debug")
370
- print(" python -m makcu --testPort COM3")
371
- print(" python -m makcu --runtest")
372
- return
373
-
374
- if args[0] == "--debug":
375
- debug_console()
376
- elif args[0] == "--testPort" and len(args) == 2:
377
- test_port(args[1])
378
- elif args[0] == "--runtest":
379
- run_tests()
380
- else:
381
- print(f"Unknown command: {' '.join(args)}")
382
- print("Usage:")
383
- print(" python -m makcu --debug")
384
- print(" python -m makcu --testPort COM3")
385
- print(" python -m makcu --runtest")
386
-
387
- if __name__ == "__main__":
1
+ import sys
2
+ import os
3
+ from pathlib import Path
4
+ from typing import List, NoReturn, Optional, Tuple
5
+ import pytest
6
+ import time
7
+ from makcu import create_controller, MakcuConnectionError, MakcuController
8
+ import json
9
+ import re
10
+ import subprocess
11
+ import makcu
12
+
13
+ makcu_version = makcu.__version__
14
+
15
+ def debug_console():
16
+ controller = create_controller(debug=True)
17
+ transport = controller.transport
18
+
19
+ print("🔧 Makcu Debug Console")
20
+ print("Type a raw command (e.g., km.version()) and press Enter.")
21
+ print("Type 'exit' or 'quit' to leave.")
22
+
23
+ command_counter = 0
24
+
25
+ while True:
26
+ try:
27
+ cmd = input(">>> ").strip()
28
+ if cmd.lower() in {"exit", "quit"}:
29
+ break
30
+ if not cmd:
31
+ continue
32
+
33
+ command_counter += 1
34
+
35
+ response = transport.send_command(cmd, expect_response=True)
36
+
37
+ if response and response.strip():
38
+ if response.strip() == cmd:
39
+ print(f"{cmd}")
40
+ else:
41
+ print(f"{response}")
42
+ else:
43
+ print("(no response)")
44
+
45
+ except Exception as e:
46
+ print(f"⚠️ Error: {e}")
47
+
48
+ controller.disconnect()
49
+ print("Disconnected.")
50
+
51
+ def test_port(port: str) -> None:
52
+ try:
53
+ print(f"Trying to connect to {port}...")
54
+ makcu = MakcuController(fallback_com_port=port, send_init=False, override_port=True)
55
+ makcu.connect()
56
+ if makcu.is_connected:
57
+ print(f"✅ Successfully connected to {port}.")
58
+ makcu.disconnect()
59
+ except MakcuConnectionError as e:
60
+ if "FileNotFoundError" in str(e):
61
+ print(f"❌ Port {port} does not exist. Please check the port name.")
62
+ else:
63
+ print(f"❌ Failed to connect to {port}: ")
64
+ except Exception as e:
65
+ print(f"❌ Unexpected error: {e}")
66
+
67
+ def check_pytest_html_installed() -> bool:
68
+ """Check if pytest-html is installed."""
69
+ try:
70
+ import pytest_html
71
+ return True
72
+ except ImportError:
73
+ return False
74
+
75
+ def find_writable_directory() -> Path:
76
+ """Find a writable directory for the HTML report."""
77
+ # Try current working directory first
78
+ cwd = Path.cwd()
79
+ if os.access(cwd, os.W_OK):
80
+ return cwd
81
+
82
+ # Try user's home directory
83
+ home = Path.home()
84
+ if os.access(home, os.W_OK):
85
+ return home
86
+
87
+ # Try temp directory as last resort
88
+ import tempfile
89
+ return Path(tempfile.gettempdir())
90
+
91
+ def parse_html_results(html_file: Path) -> Tuple[List[Tuple[str, str, int]], int]:
92
+ if not html_file.exists():
93
+ raise FileNotFoundError(f"HTML report not found: {html_file}")
94
+
95
+ with open(html_file, 'r', encoding='utf-8') as f:
96
+ content = f.read()
97
+
98
+ match = re.search(r'data-jsonblob="([^"]*)"', content)
99
+ if not match:
100
+ raise ValueError("Could not find JSON data in HTML report")
101
+
102
+ json_str = match.group(1)
103
+ json_str = json_str.replace('&#34;', '"').replace('&amp;#x27;', "'").replace('&amp;', '&')
104
+
105
+ try:
106
+ data = json.loads(json_str)
107
+ except json.JSONDecodeError as e:
108
+ raise ValueError(f"Failed to parse JSON data: {e}")
109
+
110
+ test_results = []
111
+ total_ms = 0
112
+
113
+ skip_tests = {'test_connect_to_port'}
114
+
115
+ for test_id, test_data_list in data.get('tests', {}).items():
116
+ test_name = test_id.split('::')[-1]
117
+ if test_name in skip_tests:
118
+ continue
119
+
120
+ for test_data in test_data_list:
121
+ status = test_data.get('result', 'UNKNOWN')
122
+ duration_str = test_data.get('duration', '0 ms')
123
+
124
+ duration_match = re.search(r'(\d+)\s*ms', duration_str)
125
+ duration_ms = int(duration_match.group(1)) if duration_match else 0
126
+ total_ms += duration_ms
127
+
128
+ test_results.append((test_name, status, duration_ms))
129
+
130
+ return test_results, total_ms
131
+
132
+ def run_tests() -> NoReturn:
133
+ # Check if pytest-html is installed
134
+ if not check_pytest_html_installed():
135
+ print(" pytest-html is not installed. Please install it via:")
136
+ print(" pip install pytest-html")
137
+ sys.exit(1)
138
+
139
+ try:
140
+ from rich.console import Console
141
+ from rich.table import Table
142
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
143
+ from rich.panel import Panel
144
+ from rich.align import Align
145
+ from rich import print as rprint
146
+ from rich.text import Text
147
+
148
+ console = Console()
149
+
150
+ header = Panel.fit(
151
+ f"[bold cyan]Makcu Test Suite {makcu_version}[/bold cyan]\n[dim]High-Performance Python Library[/dim]",
152
+ border_style="bright_blue"
153
+ )
154
+ console.print(Align.center(header))
155
+ console.print()
156
+
157
+ package_dir: Path = Path(__file__).resolve().parent
158
+ test_file: Path = package_dir / "test_suite.py"
159
+
160
+ # Find writable directory and create HTML path
161
+ writable_dir = find_writable_directory()
162
+ html_file: Path = writable_dir / "latest_pytest.html"
163
+
164
+ # Clean up old report if it exists
165
+ if html_file.exists():
166
+ try:
167
+ html_file.unlink()
168
+ except Exception:
169
+ pass
170
+
171
+ console.print(f"[dim]Running pytest to generate: {html_file}[/dim]")
172
+ console.print(f"[dim]Working directory: {Path.cwd()}[/dim]")
173
+
174
+ start_time = time.time()
175
+
176
+ with Progress(
177
+ SpinnerColumn(),
178
+ TextColumn("[progress.description]{task.description}"),
179
+ BarColumn(),
180
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
181
+ TimeElapsedColumn(),
182
+ console=console,
183
+ transient=True
184
+ ) as progress:
185
+ task = progress.add_task("[cyan]Running tests...", total=100)
186
+
187
+ # Run pytest with explicit output capturing
188
+ result = subprocess.run(
189
+ [
190
+ sys.executable, "-m", "pytest",
191
+ str(test_file),
192
+ "--rootdir", str(package_dir),
193
+ "-q",
194
+ "--tb=no",
195
+ "--html", str(html_file),
196
+ "--self-contained-html",
197
+ "-v" # Add verbose to help debug
198
+ ],
199
+ capture_output=True,
200
+ text=True
201
+ )
202
+
203
+ progress.update(task, completed=100)
204
+
205
+ # Check if HTML file was created
206
+ if not html_file.exists():
207
+ console.print(f"[red] HTML report was not created at: {html_file}[/red]")
208
+ console.print(f"[yellow]pytest exit code: {result.returncode}[/yellow]")
209
+ if result.stdout:
210
+ console.print("[yellow]stdout:[/yellow]")
211
+ console.print(result.stdout)
212
+ if result.stderr:
213
+ console.print("[red]stderr:[/red]")
214
+ console.print(result.stderr)
215
+
216
+ # Try to run tests without HTML report
217
+ console.print("\n[yellow]Running tests without HTML report...[/yellow]")
218
+ result2 = subprocess.run(
219
+ [sys.executable, "-m", "pytest", str(test_file), "-v"],
220
+ capture_output=True,
221
+ text=True
222
+ )
223
+ console.print(result2.stdout)
224
+ sys.exit(1)
225
+
226
+ try:
227
+ test_results, total_ms = parse_html_results(html_file)
228
+ except (FileNotFoundError, ValueError) as e:
229
+ console.print(f"[red] Failed to parse test results: {e}[/red]")
230
+ console.print(f"[yellow]⚠️ pytest exit code: {result.returncode}[/yellow]")
231
+ sys.exit(1)
232
+
233
+ elapsed_time = time.time() - start_time
234
+
235
+ table = Table(title="[bold]Test Results[/bold]", show_header=True, header_style="bold magenta")
236
+ table.add_column("Test", style="cyan", no_wrap=True)
237
+ table.add_column("Status", justify="center")
238
+ table.add_column("Time", justify="right", style="yellow")
239
+ table.add_column("Performance", justify="center")
240
+
241
+ passed = failed = skipped = 0
242
+
243
+ for test_name, status, duration_ms in test_results:
244
+ display_name = test_name.replace("test_", "").replace("_", " ").title()
245
+
246
+ if status.upper() == "PASSED":
247
+ status_text = "[green]✅ PASSED[/green]"
248
+ passed += 1
249
+ elif status.upper() == "FAILED":
250
+ status_text = "[red]❌ FAILED[/red]"
251
+ failed += 1
252
+ elif status.upper() == "SKIPPED":
253
+ status_text = "[yellow]⏭️ SKIPPED[/yellow]"
254
+ skipped += 1
255
+ else:
256
+ status_text = status
257
+
258
+ time_str = f"{duration_ms}ms" if duration_ms else "-"
259
+ if duration_ms <= 3:
260
+ perf = "[green]Excellent[/green]"
261
+ elif duration_ms <= 5:
262
+ perf = "[cyan]Great[/cyan]"
263
+ elif duration_ms <= 10:
264
+ perf = "[yellow]Good[/yellow]"
265
+ elif duration_ms > 0:
266
+ perf = "[red]🐌 Needs work[/red]"
267
+ else:
268
+ perf = "-"
269
+
270
+ table.add_row(display_name, status_text, time_str, perf)
271
+
272
+ console.print("\n")
273
+ console.print(table)
274
+ console.print()
275
+
276
+ summary = Table.grid(padding=1)
277
+ summary.add_column(style="bold cyan", justify="right")
278
+ summary.add_column(justify="left")
279
+ summary.add_row("Total Tests:", str(len(test_results)))
280
+ summary.add_row("Passed:", f"[green]{passed}[/green]")
281
+ summary.add_row("Failed:", f"[red]{failed}[/red]" if failed else str(failed))
282
+ summary.add_row("Skipped:", f"[yellow]{skipped}[/yellow]" if skipped else str(skipped))
283
+ summary.add_row("Total Time:", f"{elapsed_time:.2f}s")
284
+ summary.add_row("Avg Time/Test:", f"{total_ms/len(test_results):.1f}ms" if test_results else "0ms")
285
+
286
+ console.print(Align.center(Panel(summary, title="[bold]Summary[/bold]", border_style="blue", expand=False)))
287
+ console.print()
288
+
289
+ if test_results:
290
+ avg_time = total_ms / len(test_results)
291
+ if avg_time < 3:
292
+ perf_text = Text("Performance: ELITE - Ready for 360Hz+ gaming!", style="bold bright_green")
293
+ elif avg_time < 5:
294
+ perf_text = Text("Performance: EXCELLENT - Ready for 240Hz+ gaming!", style="bold green")
295
+ elif avg_time < 10:
296
+ perf_text = Text("Performance: GREAT - Ready for 144Hz gaming!", style="bold cyan")
297
+ else:
298
+ perf_text = Text("Performance: GOOD - Suitable for standard gaming", style="bold yellow")
299
+ else:
300
+ perf_text = Text("⚠️ No test results parsed. Check your test suite.", style="bold red")
301
+
302
+ console.print(Align.center(Panel(perf_text, border_style="green")))
303
+
304
+ # Print the location of the HTML report
305
+ console.print(f"\n[dim]HTML report saved to: {html_file}[/dim]")
306
+
307
+ sys.exit(0 if failed == 0 else 1)
308
+
309
+ except ImportError:
310
+ print("📦 Rich not installed. Install it via `pip install rich` for enhanced output.")
311
+ print("\nFallback to raw pytest output...\n")
312
+
313
+ package_dir: Path = Path(__file__).resolve().parent
314
+ test_file: Path = package_dir / "test_suite.py"
315
+
316
+ # Find writable directory
317
+ writable_dir = find_writable_directory()
318
+ html_file: Path = writable_dir / "latest_pytest.html"
319
+
320
+ print(f"HTML report will be saved to: {html_file}")
321
+
322
+ # Use subprocess instead of pytest.main for better control
323
+ result = subprocess.run(
324
+ [
325
+ sys.executable, "-m", "pytest",
326
+ str(test_file),
327
+ "--rootdir", str(package_dir),
328
+ "-q",
329
+ "--tb=no",
330
+ "--html", str(html_file),
331
+ "--self-contained-html"
332
+ ],
333
+ capture_output=True,
334
+ text=True
335
+ )
336
+
337
+ if not html_file.exists():
338
+ print(f"\n❌ HTML report was not created. pytest exit code: {result.returncode}")
339
+ if result.stdout:
340
+ print("stdout:", result.stdout)
341
+ if result.stderr:
342
+ print("stderr:", result.stderr)
343
+ sys.exit(1)
344
+
345
+ try:
346
+ test_results, total_ms = parse_html_results(html_file)
347
+ passed = sum(1 for _, status, _ in test_results if status.upper() == "PASSED")
348
+ failed = sum(1 for _, status, _ in test_results if status.upper() == "FAILED")
349
+ skipped = sum(1 for _, status, _ in test_results if status.upper() == "SKIPPED")
350
+
351
+ print(f"\n📊 Results: {passed} passed, {failed} failed, {skipped} skipped")
352
+ if test_results:
353
+ avg_time = total_ms / len(test_results)
354
+ print(f"⏱️ Average time per test: {avg_time:.1f}ms")
355
+ except (FileNotFoundError, ValueError):
356
+ print("\n⚠️ Could not parse HTML results for summary")
357
+
358
+ if result.returncode != 0:
359
+ print("\n❌ Some tests failed.")
360
+ else:
361
+ print("\n✅ All tests passed.")
362
+
363
+ sys.exit(result.returncode)
364
+
365
+ def main() -> None:
366
+ args: List[str] = sys.argv[1:]
367
+
368
+ if not args:
369
+ print("Usage:")
370
+ print(" python -m makcu --debug")
371
+ print(" python -m makcu --testPort COM3")
372
+ print(" python -m makcu --runtest")
373
+ return
374
+
375
+ if args[0] == "--debug":
376
+ debug_console()
377
+ elif args[0] == "--testPort" and len(args) == 2:
378
+ test_port(args[1])
379
+ elif args[0] == "--runtest":
380
+ run_tests()
381
+ else:
382
+ print(f"Unknown command: {' '.join(args)}")
383
+ print("Usage:")
384
+ print(" python -m makcu --debug")
385
+ print(" python -m makcu --testPort COM3")
386
+ print(" python -m makcu --runtest")
387
+
388
+ if __name__ == "__main__":
388
389
  main()