diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..e41c8a0 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,19 @@ +import os +import pytest + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixtures") + + +@pytest.fixture +def active_log(): + return os.path.join(FIXTURES_DIR, "vmstat_active.log") + + +@pytest.fixture +def standard_log(): + return os.path.join(FIXTURES_DIR, "vmstat_standard.log") + + +@pytest.fixture +def no_header_log(): + return os.path.join(FIXTURES_DIR, "vmstat_no_header.log") diff --git a/tests/fixtures/vmstat_active.log b/tests/fixtures/vmstat_active.log new file mode 100644 index 0000000..097b4d1 --- /dev/null +++ b/tests/fixtures/vmstat_active.log @@ -0,0 +1,7 @@ +procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- + r b swpd free inact active si so bi bo in cs us sy id wa st gu + 3 0 0 523 128 1059 0 0 4 1 71 111 0 0 99 0 0 0 2025-08-01 10:06:32 + 1 0 0 523 128 1059 0 0 0 0 1815 3452 4 1 96 0 0 0 2025-08-01 10:06:33 + 1 0 0 523 128 1059 0 0 0 16 2530 4711 4 3 93 0 0 0 2025-08-01 10:06:34 + 1 0 0 523 128 1059 0 0 0 0 2061 3776 4 2 94 0 0 0 2025-08-01 10:06:35 + 2 0 0 523 128 1059 0 0 0 16 1906 3617 4 1 95 0 0 0 2025-08-01 10:06:36 diff --git a/tests/fixtures/vmstat_no_header.log b/tests/fixtures/vmstat_no_header.log new file mode 100644 index 0000000..a25381d --- /dev/null +++ b/tests/fixtures/vmstat_no_header.log @@ -0,0 +1,3 @@ + 3 0 0 523 128 1059 0 0 4 1 71 111 0 0 99 0 0 0 2025-08-01 10:06:32 + 1 0 0 523 128 1059 0 0 0 0 1815 3452 4 1 96 0 0 0 2025-08-01 10:06:33 + 1 0 0 523 128 1059 0 0 0 16 2530 4711 4 3 93 0 0 0 2025-08-01 10:06:34 diff --git a/tests/fixtures/vmstat_standard.log b/tests/fixtures/vmstat_standard.log new file mode 100644 index 0000000..54bb2ec --- /dev/null +++ b/tests/fixtures/vmstat_standard.log @@ -0,0 +1,7 @@ +procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- + r b swpd free buff cache si so bi bo in cs us sy id wa st + 3 0 0 523 64 995 0 0 4 1 71 111 0 0 99 0 0 2025-08-01 10:06:32 + 1 0 0 523 64 995 0 0 0 0 1815 3452 4 1 96 0 0 2025-08-01 10:06:33 + 1 0 0 523 64 995 0 0 0 16 2530 4711 4 3 93 0 0 2025-08-01 10:06:34 + 1 0 0 523 64 995 0 0 0 0 2061 3776 4 2 94 0 0 2025-08-01 10:06:35 + 2 0 0 523 64 995 0 0 0 16 1906 3617 4 1 95 0 0 2025-08-01 10:06:36 diff --git a/tests/test_parser.py b/tests/test_parser.py new file mode 100644 index 0000000..c4f8971 --- /dev/null +++ b/tests/test_parser.py @@ -0,0 +1,110 @@ +"""Tests for the vmstat parser.""" + +import pytest +from vmstat_visualizer.parser.parser import Parser + + +class TestParserActiveFormat: + """Tests for vmstat -a output (inact/active columns) with header lines.""" + + def test_parse_entry_count(self, active_log): + p = Parser(active_log) + p.parse() + assert len(p.timeseries) == 5 + + def test_first_entry_time(self, active_log): + p = Parser(active_log) + p.parse() + assert p.timeseries[0].time == "2025-08-01 10:06:32" + + def test_first_entry_cpu(self, active_log): + p = Parser(active_log) + p.parse() + entry = p.timeseries[0] + assert entry.user_cpu_percent == "0" + assert entry.system_cpu_percent == "0" + assert entry.idle_cpu_percent == "99" + + def test_first_entry_memory(self, active_log): + p = Parser(active_log) + p.parse() + entry = p.timeseries[0] + assert entry.free_memory_kb == "523" + + def test_last_entry_time(self, active_log): + p = Parser(active_log) + p.parse() + assert p.timeseries[-1].time == "2025-08-01 10:06:36" + + +class TestParserNoHeader: + """Tests for log files without header lines (legacy format).""" + + def test_parse_entry_count(self, no_header_log): + p = Parser(no_header_log) + p.parse() + assert len(p.timeseries) == 3 + + def test_first_entry_values(self, no_header_log): + p = Parser(no_header_log) + p.parse() + entry = p.timeseries[0] + assert entry.time == "2025-08-01 10:06:32" + assert entry.run_queue == "3" + assert entry.free_memory_kb == "523" + + def test_empty_lines_skipped(self, tmp_path): + log = tmp_path / "empty.log" + log.write_text("\n\n\n") + p = Parser(str(log)) + p.parse() + assert len(p.timeseries) == 0 + + def test_header_only_file(self, tmp_path): + log = tmp_path / "header_only.log" + log.write_text( + "procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----\n" + " r b swpd free inact active si so bi bo in cs us sy id wa st\n" + ) + p = Parser(str(log)) + p.parse() + assert len(p.timeseries) == 0 + + +class TestPlotMetrics: + """Tests for the PlotMetrics inner class.""" + + def test_time_format(self, active_log): + p = Parser(active_log) + p.parse() + pm = p.PlotMetrics(p.timeseries) + assert len(pm.t) == 5 + assert pm.t[0] == "06:32" + + def test_relative_time(self, active_log): + p = Parser(active_log) + p.parse() + pm = p.PlotMetrics(p.timeseries, relative_time=True) + assert pm.t == [0, 1, 2, 3, 4] + + def test_cpu_extraction(self, active_log): + p = Parser(active_log) + p.parse() + pm = p.PlotMetrics(p.timeseries) + assert pm.user_cpu_percent[0] == "0" + assert pm.idle_cpu_percent[0] == "99" + assert len(pm.user_cpu_percent) == 5 + + def test_memory_extraction(self, active_log): + p = Parser(active_log) + p.parse() + pm = p.PlotMetrics(p.timeseries) + assert pm.free_memory_kb[0] == "523" + assert len(pm.free_memory_kb) == 5 + + def test_io_extraction(self, active_log): + p = Parser(active_log) + p.parse() + pm = p.PlotMetrics(p.timeseries) + assert pm.blocks_in[0] == "4" + assert pm.blocks_out[0] == "1" diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py new file mode 100644 index 0000000..dcbe944 --- /dev/null +++ b/tests/test_timeseries.py @@ -0,0 +1,44 @@ +"""Tests for the Timeseries data model.""" + +from vmstat_visualizer.parser.timeseries import Timeseries + + +class TestTimeseriesCommit: + """Test commit_raw_data with the default (vmstat -a) header list.""" + + def test_commit_default_headers(self): + ts = Timeseries() + data = ["1", "0", "0", "256", "128", "2048", + "0", "0", "0", "0", "50", "100", + "10", "2", "88", "0", "0", "0", "2025-01-01 12:00:01"] + ts.add_data_point(data) + ts.commit_raw_data() + + assert ts.run_queue == "1" + assert ts.free_memory_kb == "256" + assert ts.inactive_memory_kb == "128" + assert ts.active_memory_kb == "2048" + assert ts.user_cpu_percent == "10" + assert ts.idle_cpu_percent == "88" + assert ts.time == "2025-01-01 12:00:01" + + def test_add_data_point_stores_raw(self): + ts = Timeseries() + data = ["a", "b", "c"] + ts.add_data_point(data) + assert ts.raw_data == data + + def test_repr_contains_key_fields(self): + ts = Timeseries() + ts.time = "2025-01-01 12:00:00" + ts.run_queue = "3" + r = repr(ts) + assert "time=2025-01-01 12:00:00" in r + assert "run_queue=3" in r + + def test_initial_values_are_none(self): + ts = Timeseries() + assert ts.time is None + assert ts.run_queue is None + assert ts.user_cpu_percent is None + assert ts.free_memory_kb is None