delaunay 0.7.4

D-dimensional Delaunay triangulations and convex hulls in Rust, with exact predicates, multi-level validation, and bistellar flips
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
#!/usr/bin/env python3
"""
Test suite for hardware_utils.py module.

Tests hardware information detection and comparison functionality
across different platforms with proper mocking.
"""

import platform
import subprocess
from unittest.mock import Mock, mock_open, patch

import pytest

from hardware_utils import HardwareComparator, HardwareInfo


@pytest.fixture
def hardware():
    """Fixture for HardwareInfo instance."""
    return HardwareInfo()


class TestHardwareInfo:
    """Test cases for HardwareInfo class."""

    def test_init(self, hardware):
        """Test HardwareInfo initialization."""
        assert hardware.os_type == platform.system()
        assert hardware.machine == platform.machine()

    @patch("hardware_utils.platform.system")
    def test_init_with_different_os(self, mock_system):
        """Test initialization with different OS types."""
        mock_system.return_value = "Linux"
        hardware = HardwareInfo()
        assert hardware.os_type == "Linux"

    def test_run_command_empty_cmd(self, hardware):
        """Test _run_command with empty command list."""
        with pytest.raises(ValueError, match="Command list cannot be empty"):
            hardware._run_command([])

    @patch("hardware_utils.run_safe_command")
    def test_run_command_success(self, mock_run_safe, hardware):
        """Test successful command execution."""
        mock_result = Mock()
        mock_result.stdout = "test output\n"
        mock_run_safe.return_value = mock_result

        result = hardware._run_command(["echo", "test"])

        assert result == "test output"
        mock_run_safe.assert_called_once_with(
            "echo",
            ["test"],
            cwd=None,
            capture_output=True,
            text=True,
            check=True,
        )

    @patch("hardware_utils.run_safe_command")
    def test_run_command_failure(self, mock_run_safe, hardware):
        """Test command execution failure."""
        mock_run_safe.side_effect = subprocess.CalledProcessError(1, "cmd")

        with pytest.raises(subprocess.CalledProcessError):
            hardware._run_command(["false"])

    @patch("hardware_utils.platform.system")
    @patch.object(HardwareInfo, "_run_command")
    def test_get_cpu_info_darwin(self, mock_run_command, mock_system):
        """Test CPU info detection on macOS."""
        mock_system.return_value = "Darwin"
        mock_run_command.side_effect = ["Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz", "6", "12"]

        hardware = HardwareInfo()
        cpu_model, cpu_cores, cpu_threads = hardware.get_cpu_info()

        assert cpu_model == "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz"
        assert cpu_cores == "6"
        assert cpu_threads == "12"

    @patch("hardware_utils.platform.system")
    @patch("hardware_utils.shutil.which")
    @patch.object(HardwareInfo, "_run_command")
    def test_get_cpu_info_linux_with_lscpu(self, mock_run_command, mock_which, mock_system):
        """Test CPU info detection on Linux with lscpu available."""
        mock_system.return_value = "Linux"
        mock_which.side_effect = lambda cmd: cmd in ["lscpu", "nproc"]

        lscpu_output = """Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Model name:          Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
Core(s) per socket:  6
Socket(s):           1
Thread(s) per core:  2"""

        mock_run_command.side_effect = [
            lscpu_output,  # First lscpu call for model name
            lscpu_output,  # Second lscpu call for cores
            "12",  # nproc call for threads
        ]

        hardware = HardwareInfo()
        cpu_model, cpu_cores, cpu_threads = hardware.get_cpu_info()

        assert cpu_model == "Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz"
        assert cpu_cores == "6"
        assert cpu_threads == "12"

    @patch("hardware_utils.platform.system")
    @patch("hardware_utils.shutil.which")
    @patch("builtins.open", new_callable=mock_open, read_data="processor\t: 0\nmodel name\t: AMD Ryzen 5 3600\nprocessor\t: 1\n")
    def test_get_cpu_info_linux_fallback_cpuinfo(self, _mock_file, mock_which, mock_system):  # noqa: PT019
        """Test CPU info detection on Linux using /proc/cpuinfo fallback."""
        mock_system.return_value = "Linux"
        mock_which.return_value = None  # No commands available

        hardware = HardwareInfo()
        cpu_model, cpu_cores, cpu_threads = hardware.get_cpu_info()

        assert cpu_model == "AMD Ryzen 5 3600"
        assert cpu_cores == "Unknown"  # Can't determine cores without lscpu
        assert cpu_threads == "2"  # Count of processors

    @patch("hardware_utils.platform.system")
    @patch("hardware_utils.shutil.which")
    @patch.object(HardwareInfo, "_run_command")
    def test_get_cpu_info_windows(self, mock_run_command, mock_which, mock_system):
        """Test CPU info detection on Windows."""
        mock_system.return_value = "Windows"
        mock_which.side_effect = lambda cmd: cmd == "powershell"

        mock_run_command.side_effect = ["Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz", "8", "16"]

        hardware = HardwareInfo()
        cpu_model, cpu_cores, cpu_threads = hardware.get_cpu_info()

        assert cpu_model == "Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz"
        assert cpu_cores == "8"
        assert cpu_threads == "16"

    @patch("hardware_utils.platform.system")
    def test_get_cpu_info_unknown_os(self, mock_system):
        """Test CPU info detection on unknown OS."""
        mock_system.return_value = "UnknownOS"

        hardware = HardwareInfo()
        cpu_model, cpu_cores, cpu_threads = hardware.get_cpu_info()

        assert cpu_model == "Unknown"
        assert cpu_cores == "Unknown"
        assert cpu_threads == "Unknown"

    @patch("hardware_utils.platform.system")
    @patch.object(HardwareInfo, "_run_command")
    def test_get_cpu_info_command_failure(self, mock_run_command, mock_system):
        """Test CPU info detection when commands fail."""
        mock_system.return_value = "Darwin"
        mock_run_command.side_effect = subprocess.CalledProcessError(1, "cmd")

        hardware = HardwareInfo()
        cpu_model, cpu_cores, cpu_threads = hardware.get_cpu_info()

        assert cpu_model == "Unknown"
        assert cpu_cores == "Unknown"
        assert cpu_threads == "Unknown"

    @patch("hardware_utils.platform.system")
    @patch.object(HardwareInfo, "_run_command")
    def test_get_memory_info_darwin(self, mock_run_command, mock_system):
        """Test memory info detection on macOS."""
        mock_system.return_value = "Darwin"
        mock_run_command.return_value = "17179869184"  # 16 GB in bytes

        hardware = HardwareInfo()
        memory = hardware.get_memory_info()

        assert memory == "16.0 GB"

    @patch("hardware_utils.platform.system")
    @patch("builtins.open", new_callable=mock_open, read_data="MemTotal:       16384000 kB\n")
    def test_get_memory_info_linux(self, _mock_file, mock_system):  # noqa: PT019
        """Test memory info detection on Linux."""
        mock_system.return_value = "Linux"

        hardware = HardwareInfo()
        memory = hardware.get_memory_info()

        assert memory == "15.6 GB"  # 16384000 kB converted to GB

    @patch("hardware_utils.platform.system")
    @patch("hardware_utils.shutil.which")
    @patch.object(HardwareInfo, "_run_command")
    def test_get_memory_info_windows(self, mock_run_command, mock_which, mock_system):
        """Test memory info detection on Windows."""
        mock_system.return_value = "Windows"
        mock_which.side_effect = lambda cmd: cmd == "powershell"
        mock_run_command.return_value = "32.0 GB"

        hardware = HardwareInfo()
        memory = hardware.get_memory_info()

        assert memory == "32.0 GB"

    @patch("hardware_utils.platform.system")
    def test_get_memory_info_unknown_os(self, mock_system):
        """Test memory info detection on unknown OS."""
        mock_system.return_value = "UnknownOS"

        hardware = HardwareInfo()
        memory = hardware.get_memory_info()

        assert memory == "Unknown"

    @patch("hardware_utils.shutil.which")
    @patch.object(HardwareInfo, "_run_command")
    def test_get_rust_info_success(self, mock_run_command, mock_which, hardware):
        """Test Rust info detection when rustc is available."""
        mock_which.return_value = "/usr/bin/rustc"
        mock_run_command.side_effect = ["rustc 1.70.0 (90c541806 2023-05-31)", "rustc 1.70.0 (90c541806 2023-05-31)\nhost: x86_64-apple-darwin\n"]

        rust_version, rust_target = hardware.get_rust_info()

        assert rust_version == "rustc 1.70.0 (90c541806 2023-05-31)"
        assert rust_target == "x86_64-apple-darwin"

    @patch("hardware_utils.shutil.which")
    def test_get_rust_info_no_rustc(self, mock_which, hardware):
        """Test Rust info detection when rustc is not available."""
        mock_which.return_value = None

        rust_version, rust_target = hardware.get_rust_info()

        assert rust_version == "Unknown"
        assert rust_target == "Unknown"

    @patch("hardware_utils.shutil.which")
    @patch.object(HardwareInfo, "_run_command")
    def test_get_rust_info_command_failure(self, mock_run_command, mock_which, hardware):
        """Test Rust info detection when rustc commands fail."""
        mock_which.return_value = "/usr/bin/rustc"
        mock_run_command.side_effect = subprocess.CalledProcessError(1, "cmd")

        rust_version, rust_target = hardware.get_rust_info()

        assert rust_version == "Unknown"
        assert rust_target == "Unknown"

    def test_get_hardware_info(self, hardware):
        """Test comprehensive hardware info collection."""
        with (
            patch.object(hardware, "get_cpu_info") as mock_cpu,
            patch.object(hardware, "get_memory_info") as mock_memory,
            patch.object(hardware, "get_rust_info") as mock_rust,
        ):
            mock_cpu.return_value = ("Intel i7", "8", "16")
            mock_memory.return_value = "16.0 GB"
            mock_rust.return_value = ("rustc 1.70.0", "x86_64-unknown-linux-gnu")

            info = hardware.get_hardware_info()

            expected_keys = ["OS", "CPU", "CPU_CORES", "CPU_THREADS", "MEMORY", "RUST", "TARGET"]
            assert set(info.keys()) == set(expected_keys)
            assert info["CPU"] == "Intel i7"
            assert info["CPU_CORES"] == "8"
            assert info["CPU_THREADS"] == "16"
            assert info["MEMORY"] == "16.0 GB"
            assert info["RUST"] == "rustc 1.70.0"
            assert info["TARGET"] == "x86_64-unknown-linux-gnu"

    @pytest.mark.parametrize(
        ("system_name", "expected_os"), [("Darwin", "macOS"), ("Linux", "Linux"), ("Windows", "Windows"), ("FreeBSD", "Unknown (FreeBSD)")]
    )
    @patch("hardware_utils.platform.system")
    def test_get_hardware_info_os_mapping(self, mock_system, system_name, expected_os):
        """Test OS name mapping in hardware info."""
        mock_system.return_value = system_name
        hardware = HardwareInfo()

        with (
            patch.object(hardware, "get_cpu_info") as mock_cpu,
            patch.object(hardware, "get_memory_info") as mock_memory,
            patch.object(hardware, "get_rust_info") as mock_rust,
        ):
            mock_cpu.return_value = ("CPU", "4", "8")
            mock_memory.return_value = "8.0 GB"
            mock_rust.return_value = ("rustc 1.70.0", "target")

            info = hardware.get_hardware_info()
            assert info["OS"] == expected_os

    def test_format_hardware_info(self, hardware):
        """Test hardware info formatting."""
        test_info = {
            "OS": "macOS",
            "CPU": "Intel i7",
            "CPU_CORES": "8",
            "CPU_THREADS": "16",
            "MEMORY": "16.0 GB",
            "RUST": "rustc 1.70.0",
            "TARGET": "x86_64-apple-darwin",
        }

        formatted = hardware.format_hardware_info(test_info)

        assert "Hardware Information:" in formatted
        assert "OS: macOS" in formatted
        assert "CPU: Intel i7" in formatted
        assert "CPU Cores: 8" in formatted
        assert "CPU Threads: 16" in formatted
        assert "Memory: 16.0 GB" in formatted
        assert "Rust: rustc 1.70.0" in formatted
        assert "Target: x86_64-apple-darwin" in formatted

    def test_format_hardware_info_none(self, hardware):
        """Test hardware info formatting with None input."""
        with patch.object(hardware, "get_hardware_info") as mock_get_info:
            mock_get_info.return_value = {
                "OS": "Linux",
                "CPU": "AMD Ryzen",
                "CPU_CORES": "6",
                "CPU_THREADS": "12",
                "MEMORY": "32.0 GB",
                "RUST": "rustc 1.70.0",
                "TARGET": "x86_64-unknown-linux-gnu",
            }

            formatted = hardware.format_hardware_info(None)
            assert "OS: Linux" in formatted


class TestHardwareComparator:
    """Test cases for HardwareComparator class."""

    def test_parse_baseline_hardware_complete(self):
        """Test parsing complete baseline hardware info."""
        baseline_content = """Benchmark Results
Generated on: 2023-06-15 10:30:00

Hardware Information:
  OS: macOS
  CPU: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
  CPU Cores: 6
  CPU Threads: 12
  Memory: 16.0 GB
  Rust: rustc 1.70.0 (90c541806 2023-05-31)
  Target: x86_64-apple-darwin

Benchmark Results:
test_benchmark ... 1.234 ms
"""

        info = HardwareComparator.parse_baseline_hardware(baseline_content)

        expected = {
            "OS": "macOS",
            "CPU": "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz",
            "CPU_CORES": "6",
            "CPU_THREADS": "12",
            "MEMORY": "16.0 GB",
            "RUST": "rustc 1.70.0 (90c541806 2023-05-31)",
            "TARGET": "x86_64-apple-darwin",
        }

        assert info == expected

    def test_parse_baseline_hardware_partial(self):
        """Test parsing partial baseline hardware info."""
        baseline_content = """Hardware Information:
  OS: Linux
  CPU: AMD Ryzen 5 3600
  Memory: 32.0 GB

Other content here...
"""

        info = HardwareComparator.parse_baseline_hardware(baseline_content)

        assert info["OS"] == "Linux"
        assert info["CPU"] == "AMD Ryzen 5 3600"
        assert info["MEMORY"] == "32.0 GB"
        assert info["CPU_CORES"] == "Unknown"  # Not specified
        assert info["RUST"] == "Unknown"  # Not specified

    def test_parse_baseline_hardware_empty(self):
        """Test parsing baseline with no hardware info."""
        baseline_content = "No hardware information found"

        info = HardwareComparator.parse_baseline_hardware(baseline_content)

        # Should return all "Unknown" values
        for value in info.values():
            assert value == "Unknown"

    def test_compare_hardware_identical(self):
        """Test hardware comparison with identical configurations."""
        current_info = {
            "OS": "macOS",
            "CPU": "Intel i7",
            "CPU_CORES": "8",
            "CPU_THREADS": "16",
            "MEMORY": "16.0 GB",
            "RUST": "rustc 1.70.0",
            "TARGET": "x86_64-apple-darwin",
        }

        baseline_info = current_info.copy()

        report, has_warnings = HardwareComparator.compare_hardware(current_info, baseline_info)

        assert not has_warnings
        assert "Hardware configurations are compatible" in report

    def test_compare_hardware_different_os(self):
        """Test hardware comparison with different OS."""
        current_info = {
            "OS": "Linux",
            "CPU": "Intel i7",
            "CPU_CORES": "8",
            "CPU_THREADS": "16",
            "MEMORY": "16.0 GB",
            "RUST": "rustc 1.70.0",
            "TARGET": "x86_64-unknown-linux-gnu",
        }

        baseline_info = current_info.copy()
        baseline_info["OS"] = "macOS"

        report, has_warnings = HardwareComparator.compare_hardware(current_info, baseline_info)

        assert has_warnings
        assert "OS differs: Linux vs macOS" in report

    def test_compare_hardware_different_cpu(self):
        """Test hardware comparison with different CPU."""
        current_info = {
            "OS": "Linux",
            "CPU": "AMD Ryzen 5 3600",
            "CPU_CORES": "6",
            "CPU_THREADS": "12",
            "MEMORY": "16.0 GB",
            "RUST": "rustc 1.70.0",
            "TARGET": "x86_64-unknown-linux-gnu",
        }

        baseline_info = current_info.copy()
        baseline_info["CPU"] = "Intel i7-8700K"

        report, has_warnings = HardwareComparator.compare_hardware(current_info, baseline_info)

        assert has_warnings
        assert "CPU differs:" in report
        assert "results may not be directly comparable" in report

    def test_compare_hardware_different_cores(self):
        """Test hardware comparison with different core counts."""
        current_info = {
            "OS": "Linux",
            "CPU": "Intel i7",
            "CPU_CORES": "8",
            "CPU_THREADS": "16",
            "MEMORY": "16.0 GB",
            "RUST": "rustc 1.70.0",
            "TARGET": "x86_64-unknown-linux-gnu",
        }

        baseline_info = current_info.copy()
        baseline_info["CPU_CORES"] = "6"

        report, has_warnings = HardwareComparator.compare_hardware(current_info, baseline_info)

        assert has_warnings
        assert "CPU core count differs: 8 vs 6 cores" in report

    def test_compare_hardware_memory_tolerance(self):
        """Test memory comparison with numeric (percentage-based) tolerance."""
        current_info = {
            "OS": "Linux",
            "CPU": "Intel i7",
            "CPU_CORES": "8",
            "CPU_THREADS": "16",
            "MEMORY": "16.0 GB",
            "RUST": "rustc 1.70.0",
            "TARGET": "x86_64-unknown-linux-gnu",
        }

        # Test within tolerance (~0.3% difference)
        baseline_info = current_info.copy()
        baseline_info["MEMORY"] = "15.95 GB"

        report, has_warnings = HardwareComparator.compare_hardware(current_info, baseline_info)
        assert not has_warnings

        # Test outside tolerance (>2% difference)
        baseline_info["MEMORY"] = "15.6 GB"

        report, has_warnings = HardwareComparator.compare_hardware(current_info, baseline_info)
        assert has_warnings
        assert "Memory differs:" in report

    def test_compare_hardware_unknown_baseline(self):
        """Test hardware comparison with unknown baseline values."""
        current_info = {
            "OS": "Linux",
            "CPU": "Intel i7",
            "CPU_CORES": "8",
            "CPU_THREADS": "16",
            "MEMORY": "16.0 GB",
            "RUST": "rustc 1.70.0",
            "TARGET": "x86_64-unknown-linux-gnu",
        }

        baseline_info = {
            "OS": "Unknown",
            "CPU": "Unknown",
            "CPU_CORES": "Unknown",
            "CPU_THREADS": "Unknown",
            "MEMORY": "Unknown",
            "RUST": "Unknown",
            "TARGET": "Unknown",
        }

        report, has_warnings = HardwareComparator.compare_hardware(current_info, baseline_info)

        # Should not report warnings when baseline is unknown
        assert not has_warnings
        assert "Hardware configurations are compatible" in report

    @pytest.mark.parametrize(
        ("memory_str", "expected"),
        [
            ("16.0 GB", 16.0),
            ("32.5 GB", 32.5),
            ("8GB", 8.0),
            ("64 GB", 64.0),
            ("16,5 GB", 16.5),  # Comma decimal separator
            ("1.0 TB", 1.0),  # Larger unit still parses numeric part
            ("Unknown", None),
            ("Invalid format", None),
            ("", None),
        ],
    )
    def test_extract_memory_value(self, memory_str, expected):
        """Test memory value extraction from strings."""
        result = HardwareComparator._extract_memory_value(memory_str)
        if expected is None:
            assert result is None
        else:
            assert result == pytest.approx(expected, abs=1e-9)


class TestHardwareUtilsIntegration:
    """Integration tests for hardware_utils functionality."""

    def test_real_hardware_info_structure(self):
        """Test that real hardware info returns expected structure."""
        hardware = HardwareInfo()
        info = hardware.get_hardware_info()

        expected_keys = {"OS", "CPU", "CPU_CORES", "CPU_THREADS", "MEMORY", "RUST", "TARGET"}
        assert set(info.keys()) == expected_keys

        # All values should be strings
        for key, value in info.items():
            assert isinstance(value, str), f"Key {key} should have string value"

    def test_cpu_info_returns_tuples(self):
        """Test that CPU info methods return proper tuple structure."""
        hardware = HardwareInfo()

        cpu_model, cpu_cores, cpu_threads = hardware.get_cpu_info()
        rust_version, rust_target = hardware.get_rust_info()

        assert isinstance(cpu_model, str)
        assert isinstance(cpu_cores, str)
        assert isinstance(cpu_threads, str)
        assert isinstance(rust_version, str)
        assert isinstance(rust_target, str)

    def test_memory_info_returns_string(self):
        """Test that memory info returns a string."""
        hardware = HardwareInfo()
        memory = hardware.get_memory_info()

        assert isinstance(memory, str)

    def test_formatted_output_structure(self):
        """Test that formatted output has expected structure."""
        hardware = HardwareInfo()
        formatted = hardware.format_hardware_info()

        assert isinstance(formatted, str)
        assert "Hardware Information:" in formatted

        # Should contain all expected fields
        expected_fields = ["OS:", "CPU:", "CPU Cores:", "CPU Threads:", "Memory:", "Rust:", "Target:"]
        for field in expected_fields:
            assert field in formatted