"""Cross-port contract: Python/ctypes and Kotlin/JNI consume the same fixtures.""" import ctypes import json import math import os from pathlib import Path import unittest from protocan.plot import Bounds, Marker, Markers, PlotMath, Viewport @unittest.skipUnless(os.environ.get("SETPROTOCOL_LIBRARY"), "Set SETPROTOCOL_LIBRARY to the built C library") class PlotTests(unittest.TestCase): @classmethod def setUpClass(cls): cls.core = PlotMath(ctypes.CDLL(os.environ["SETPROTOCOL_LIBRARY"])) def test_shared_numeric_fixtures(self): path = Path(__file__).resolve().parents[2] / "c/set-protocol/tests/fixtures/plot-v1.json" for case in json.loads(path.read_text())["cases"]: with self.subTest(case=case["name"]): if case["output"] is None: with self.assertRaises(ValueError): self.core.call(case["op"], *case["input"]) else: actual = self.core.call(case["op"], *case["input"]) self.assertEqual(len(actual), len(case["output"])) for a, b in zip(actual, case["output"]): self.assertAlmostEqual(a, b, places=10) def test_markers_stay_in_data_coordinates_and_cross(self): bounds = Bounds(1000, 2000, -10, 10) markers = Markers().positioned(self.core, bounds) zoomed = bounds.visible(self.core, Viewport(.25, .25, .5, .5)) self.assertEqual(markers, markers.positioned(self.core, zoomed)) moved = markers.drag(self.core, Marker.A, 50, 500, zoomed) self.assertAlmostEqual(moved.a, markers.a + 50) self.assertEqual(markers.b, moved.b) self.assertEqual(Marker.A, moved.hit(self.core, 0, 50, 500, 100, 20, zoomed)) crossed = markers.move(Marker.A, 1900).move(Marker.B, 1100) self.assertEqual(-800, self.core.delta(crossed.a, crossed.b)) def test_invalid_numeric_inputs_do_not_escape_to_painter(self): for value in (math.nan, math.inf, -math.inf): self.assertEqual(Viewport(), Viewport().transform(self.core, zoom_x=value)) with self.assertRaises(ValueError): self.core.pinch_axis(value, 10, 8) if __name__ == "__main__": unittest.main()