diff --git a/tests/test_ggml.py b/tests/test_ggml.py index bff3f61..649e192 100644 --- a/tests/test_ggml.py +++ b/tests/test_ggml.py @@ -1240,10 +1240,22 @@ class WindowsOwnership(Local): class Machine(Local): """What this machine can hold, and what that makes worth pointing at.""" + def _sysconf(self, phys_pages, page_size=4096): + """Stand where sysconf answers whatever this test wants it to. + + `create` because Windows has no os.sysconf at all, and a patch that + insists on the real attribute fails there before the test runs. What + the code under test does about that absence is two lines down from + what these are checking, and it is checked on its own below. + """ + return mock.patch.object( + ggml.os, "sysconf", create=True, + side_effect=lambda name: (page_size if name == "SC_PAGE_SIZE" + else phys_pages)) + def test_the_memory_is_read_the_way_each_system_reports_it(self): # Linux and most Macs answer through sysconf. - with mock.patch.object(ggml.os, "sysconf", lambda name: - 4096 if name == "SC_PAGE_SIZE" else 4_194_304): + with self._sysconf(4_194_304): self.assertEqual(ggml.total_memory(), 16 * ggml.GB) def test_a_mac_without_the_page_count_is_asked_for_the_number(self): @@ -1253,7 +1265,8 @@ class Machine(Local): self.assertEqual(args, ["sysctl", "-n", "hw.memsize"]) return mock.Mock(stdout=f"{32 * ggml.GB}\n") - with mock.patch.object(ggml.os, "sysconf", side_effect=ValueError), \ + with mock.patch.object(ggml.os, "sysconf", create=True, + side_effect=ValueError), \ mock.patch.object(sys, "platform", "darwin"), \ mock.patch.object(ggml.subprocess, "run", answer): self.assertEqual(ggml.total_memory(), 32 * ggml.GB) @@ -1263,8 +1276,7 @@ class Machine(Local): # CPython hands that back rather than raising, so the product came out # negative: a 64 GB workstation was told every model past 512 MB was # too big for it, and the machine line read "Memory: -4096 B". - with mock.patch.object(ggml.os, "sysconf", lambda name: - 4096 if name == "SC_PAGE_SIZE" else -1): + with self._sysconf(-1): self.assertEqual(ggml.total_memory(), 0) self.assertTrue(ggml.fits(574 << 20, memory=0)) @@ -1279,7 +1291,8 @@ class Machine(Local): self.assertEqual(len(calls), 1) def test_a_system_that_answers_nothing_is_an_unknown_machine(self): - with mock.patch.object(ggml.os, "sysconf", side_effect=ValueError), \ + with mock.patch.object(ggml.os, "sysconf", create=True, + side_effect=ValueError), \ mock.patch.object(sys, "platform", "linux"): self.assertEqual(ggml.total_memory(), 0) diff --git a/tests/test_ui.py b/tests/test_ui.py index 8e18db5..b215987 100644 --- a/tests/test_ui.py +++ b/tests/test_ui.py @@ -1245,6 +1245,16 @@ class LocalModels(DikteTest): def _repos(box): return [box.repo.itemText(row) for row in range(box.repo.count())] + @staticmethod + def _roomy(): + """Stand on a machine with room for every suggestion. + + The order the publishers come in follows the memory, so a test that + reads it has to say which machine it is standing on. A build runner + with 7 GB in it puts the two Gemma 4 rows last and is right to. + """ + return mock.patch.object(ggml, "total_memory", return_value=64 << 30) + @staticmethod def _offered(box): """The model names in the box, headings and duplicates left out.""" @@ -1377,12 +1387,13 @@ class LocalModels(DikteTest): # reading the rows back the way a finished download does was doubling # it in the list every time. box = self.window(cfg.Config()).local_whisper - box._on_listed([("models", [ - self._item("ggml-tiny.bin", 77 << 20), - self._item("ggml-large-v3-turbo-q5_0.bin", 574 << 20), - ], "")], "") - before = self._offered(box) - box._fill_models_from_current() + with self._roomy(): + box._on_listed([("models", [ + self._item("ggml-tiny.bin", 77 << 20), + self._item("ggml-large-v3-turbo-q5_0.bin", 574 << 20), + ], "")], "") + before = self._offered(box) + box._fill_models_from_current() self.assertEqual(self._offered(box), before) names = [box.model.itemData(row) for row in range(box.model.count())] self.assertEqual(len([n for n in names if n]), len(before) + 1) @@ -1423,9 +1434,10 @@ class LocalModels(DikteTest): def test_only_the_suggested_publishers_are_offered_to_start_with(self): # Forty repository ids is not a choice anybody can make. box = self.window(cfg.Config()).local_llm - box._on_listed([("repos", [ggml.SUGGESTED_LLM[0], - "ggml-org/something-else-GGUF"], "")], "") - self.assertEqual(self._repos(box), list(ggml.SUGGESTED_LLM)) + with self._roomy(): + box._on_listed([("repos", [ggml.SUGGESTED_LLM[0], + "ggml-org/something-else-GGUF"], "")], "") + self.assertEqual(self._repos(box), list(ggml.SUGGESTED_LLM)) def test_a_suggestion_missing_from_the_listing_is_still_offered(self): # The listing is the forty repositories touched most recently, and a @@ -1437,10 +1449,11 @@ class LocalModels(DikteTest): def test_the_switch_brings_the_rest_and_keeps_them_apart(self): box = self.window(cfg.Config()).local_llm - box._on_listed([("repos", [ggml.SUGGESTED_LLM[0], - "ggml-org/something-else-GGUF"], "")], "") - box.every_repo.setChecked(True) - rows = self._repos(box) + with self._roomy(): + box._on_listed([("repos", [ggml.SUGGESTED_LLM[0], + "ggml-org/something-else-GGUF"], "")], "") + box.every_repo.setChecked(True) + rows = self._repos(box) self.assertEqual(rows[:len(ggml.SUGGESTED_LLM)], list(ggml.SUGGESTED_LLM)) # A separator rather than a heading: the box is typed into as well as