|
5 | 5 | // Created by The Northstar✨ System on 2023-10-26. |
6 | 6 | // |
7 | 7 |
|
8 | | -import XCTest |
| 8 | +import Foundation |
| 9 | +import CoreGraphics |
| 10 | +#if canImport(CoreImage) |
| 11 | +import CoreImage |
| 12 | +#endif |
| 13 | +import Testing |
| 14 | + |
9 | 15 | import RectangleTools |
10 | 16 |
|
11 | 17 |
|
12 | 18 |
|
13 | | -final class Point_Tests: XCTestCase { |
| 19 | +@Suite("Point Tests") |
| 20 | +final class Point_Tests { |
14 | 21 |
|
| 22 | + @Test |
15 | 23 | func testPointToPointDistance() { |
16 | | - XCTAssertEqual(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: 1, y: 1)), sqrt(2)) |
17 | | - XCTAssertEqual(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: -1, y: 1)), sqrt(2)) |
18 | | - XCTAssertEqual(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: 1, y: -1)), sqrt(2)) |
19 | | - XCTAssertEqual(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: -1, y: -1)), sqrt(2)) |
| 24 | + #expect(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: 1, y: 1)) == CGFloat(sqrt(2))) |
| 25 | + #expect(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: -1, y: 1)) == CGFloat(sqrt(2))) |
| 26 | + #expect(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: 1, y: -1)) == CGFloat(sqrt(2))) |
| 27 | + #expect(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: -1, y: -1)) == CGFloat(sqrt(2))) |
20 | 28 | } |
21 | 29 |
|
22 | 30 |
|
| 31 | + @Test |
23 | 32 | func testSizeExtremitiesDistance() { |
24 | 33 | var cgSize = CGSize(width: 1, height: 1) |
25 | | - XCTAssertEqual(cgSize.minXminY().distance(to: cgSize.maxXmaxY()), sqrt(2)) |
| 34 | + #expect(cgSize.minXminY().distance(to: cgSize.maxXmaxY()) == CGFloat(sqrt(2))) |
26 | 35 |
|
27 | 36 | cgSize = CGSize(width: -1, height: 1) |
28 | | - XCTAssertEqual(cgSize.minXminY().distance(to: cgSize.maxXmaxY()), sqrt(2)) |
| 37 | + #expect(cgSize.minXminY().distance(to: cgSize.maxXmaxY()) == CGFloat(sqrt(2))) |
29 | 38 |
|
30 | 39 | cgSize = CGSize(width: 1, height: -1) |
31 | | - XCTAssertEqual(cgSize.minXminY().distance(to: cgSize.maxXmaxY()), sqrt(2)) |
| 40 | + #expect(cgSize.minXminY().distance(to: cgSize.maxXmaxY()) == CGFloat(sqrt(2))) |
32 | 41 |
|
33 | 42 | cgSize = CGSize(width: -1, height: -1) |
34 | | - XCTAssertEqual(cgSize.minXminY().distance(to: cgSize.maxXmaxY()), sqrt(2)) |
| 43 | + #expect(cgSize.minXminY().distance(to: cgSize.maxXmaxY()) == CGFloat(sqrt(2))) |
35 | 44 | } |
36 | 45 |
|
37 | 46 |
|
| 47 | + @Test |
38 | 48 | func testRectExtremitiesDistance() { |
39 | 49 | var cgRect = CGRect(x: 0, y: 0, width: 1, height: 1) |
40 | | - XCTAssertEqual(cgRect.minXminY.distance(to: cgRect.maxXmaxY), sqrt(2)) |
| 50 | + #expect(cgRect.minXminY.distance(to: cgRect.maxXmaxY) == CGFloat(sqrt(2))) |
41 | 51 |
|
42 | 52 | cgRect = CGRect(x: 0, y: 0, width: -1, height: 1) |
43 | | - XCTAssertEqual(cgRect.minXminY.distance(to: cgRect.maxXmaxY), sqrt(2)) |
| 53 | + #expect(cgRect.minXminY.distance(to: cgRect.maxXmaxY) == CGFloat(sqrt(2))) |
44 | 54 |
|
45 | 55 | cgRect = CGRect(x: 0, y: 0, width: 1, height: -1) |
46 | | - XCTAssertEqual(cgRect.minXminY.distance(to: cgRect.maxXmaxY), sqrt(2)) |
| 56 | + #expect(cgRect.minXminY.distance(to: cgRect.maxXmaxY) == CGFloat(sqrt(2))) |
47 | 57 |
|
48 | 58 | cgRect = CGRect(x: 0, y: 0, width: -1, height: -1) |
49 | | - XCTAssertEqual(cgRect.minXminY.distance(to: cgRect.maxXmaxY), sqrt(2)) |
| 59 | + #expect(cgRect.minXminY.distance(to: cgRect.maxXmaxY) == CGFloat(sqrt(2))) |
50 | 60 |
|
51 | 61 |
|
52 | 62 | cgRect = CGRect(x: .random(in: -1000 ... 1000), y: .random(in: -1000 ... 1000), width: 1, height: 1) |
53 | | - XCTAssertEqual(cgRect.minXminY.distance(to: cgRect.maxXmaxY), sqrt(2)) |
| 63 | + #expect(cgRect.minXminY.distance(to: cgRect.maxXmaxY) == CGFloat(sqrt(2))) |
54 | 64 |
|
55 | 65 | cgRect = CGRect(x: .random(in: -1000 ... 1000), y: .random(in: -1000 ... 1000), width: -1, height: 1) |
56 | | - XCTAssertEqual(cgRect.minXminY.distance(to: cgRect.maxXmaxY), sqrt(2)) |
| 66 | + #expect(cgRect.minXminY.distance(to: cgRect.maxXmaxY) == CGFloat(sqrt(2))) |
57 | 67 |
|
58 | 68 | cgRect = CGRect(x: .random(in: -1000 ... 1000), y: .random(in: -1000 ... 1000), width: 1, height: -1) |
59 | | - XCTAssertEqual(cgRect.minXminY.distance(to: cgRect.maxXmaxY), sqrt(2)) |
| 69 | + #expect(cgRect.minXminY.distance(to: cgRect.maxXmaxY) == CGFloat(sqrt(2))) |
60 | 70 |
|
61 | 71 | cgRect = CGRect(x: .random(in: -1000 ... 1000), y: .random(in: -1000 ... 1000), width: -1, height: -1) |
62 | | - XCTAssertEqual(cgRect.minXminY.distance(to: cgRect.maxXmaxY), sqrt(2)) |
| 72 | + #expect(cgRect.minXminY.distance(to: cgRect.maxXmaxY) == CGFloat(sqrt(2))) |
63 | 73 | } |
64 | 74 |
|
65 | 75 |
|
| 76 | + @Test |
66 | 77 | func testMagnitude() { |
67 | 78 | // https://www.wolframalpha.com/input?i=distance+from+%28-2%2C-1%29+to+%285%2C6%29 |
68 | 79 | #if canImport(CoreImage) |
69 | | - XCTAssertEqual(CIVector(x: -2, y: -1, z: 5, w: 6).magnitude, 7 * sqrt(2)) |
| 80 | + #expect(CIVector(x: -2, y: -1, z: 5, w: 6).magnitude == 7 * sqrt(2)) |
70 | 81 | #endif |
71 | 82 | } |
| 83 | + |
| 84 | + |
| 85 | + @Test |
| 86 | + func testPointOffset() { |
| 87 | + // Basics |
| 88 | + #expect(CGPoint(x: 0, y: 0).offset(dx: 10, dy: 20) == .init(x: 10, y: 20)) |
| 89 | + #expect(CGPoint(x: 0, y: 0).offset(dx: -10, dy: -20) == .init(x: -10, y: -20)) |
| 90 | + |
| 91 | + #expect(CGPoint.zero.offset(by: CGPoint(x: 10, y: 20)) == .init(x: 10, y: 20)) |
| 92 | + #expect(CGPoint.zero + CGPoint(x: 10, y: 20) == .init(x: 10, y: 20)) |
| 93 | + #expect(CGPoint.zero.offset(by: CGPoint(x: -10, y: -20)) == .init(x: -10, y: -20)) |
| 94 | + #expect(CGPoint.zero + CGPoint(x: -10, y: -20) == .init(x: -10, y: -20)) |
| 95 | + |
| 96 | + |
| 97 | + // Fuzzing |
| 98 | + for _ in 0 ..< 20 { |
| 99 | + let point = CGPoint(x: .random(in: -1000 ... 1000), y: .random(in: -1000 ... 1000)) |
| 100 | + let offset = CGPoint(x: .random(in: -1000 ... 1000), y: .random(in: -1000 ... 1000)) |
| 101 | + let expected = CGPoint(x: point.x + offset.x, y: point.y + offset.y) |
| 102 | + |
| 103 | + #expect(point.offset(dx: offset.x, dy: offset.y) == expected) |
| 104 | + #expect(point.offset(by: offset) == expected) |
| 105 | + #expect(point + offset == expected) |
| 106 | + } |
| 107 | + |
| 108 | + // LLM-generated tests |
| 109 | + do { |
| 110 | + // Zero offset preserves the point |
| 111 | + #expect(CGPoint(x: 5, y: -3).offset(dx: 0, dy: 0) == CGPoint(x: 5, y: -3)) |
| 112 | + #expect(CGPoint(x: 5, y: -3).offset(by: CGPoint.zero) == CGPoint(x: 5, y: -3)) |
| 113 | + #expect(CGPoint(x: 5, y: -3) + .zero == CGPoint(x: 5, y: -3)) |
| 114 | + #expect(CGPoint.zero.offset(by: CGPoint(x: 0, y: 0)) == CGPoint.zero) |
| 115 | + |
| 116 | + // Offset by identical point doubles coordinates |
| 117 | + let point = CGPoint(x: 2, y: 3) |
| 118 | + #expect(point.offset(by: point) == CGPoint(x: 4, y: 6)) |
| 119 | + #expect(point + point == CGPoint(x: 4, y: 6)) |
| 120 | + |
| 121 | + // Offset that results in the origin |
| 122 | + #expect(CGPoint(x: -1, y: -2).offset(by: CGPoint(x: 1, y: 2)) == CGPoint.zero) |
| 123 | + |
| 124 | + // Offset with negative zero (no‑op) |
| 125 | + #expect(CGPoint(x: 3, y: 4).offset(dx: -0, dy: -0) == CGPoint(x: 3, y: 4)) |
| 126 | + let minusZero = CGFloat(-0.0) |
| 127 | + #expect(CGPoint(x: 5, y: 5).offset(dx: minusZero, dy: minusZero) == CGPoint(x: 5, y: 5)) |
| 128 | + |
| 129 | + // Combination of positive and negative components |
| 130 | + #expect(CGPoint(x: -10, y: 10).offset(dx: 15, dy: -5) == CGPoint(x: 5, y: 5)) |
| 131 | + |
| 132 | + // Edge cases: infinities |
| 133 | + let inf = CGFloat.infinity |
| 134 | + let pInf = CGPoint(x: 10, y: -20).offset(dx: inf, dy: -inf) |
| 135 | + #expect(pInf.x.isInfinite) |
| 136 | + #expect(pInf.y.isInfinite && pInf.y.sign == .minus) |
| 137 | + |
| 138 | + // Edge cases: NaNs |
| 139 | + let nanPt = CGPoint(x: CGFloat.nan, y: .nan).offset(dx: 1, dy: 1) |
| 140 | + #expect(nanPt.x.isNaN) |
| 141 | + #expect(nanPt.y.isNaN) |
| 142 | + |
| 143 | + // Offset a point containing a NaN |
| 144 | + var ptWithNan = CGPoint(x: 5, y: CGFloat.nan).offset(by: CGPoint(x: 0, y: 1)) |
| 145 | + #expect(ptWithNan.y.isNaN) |
| 146 | + ptWithNan = CGPoint(x: 5, y: CGFloat.nan) + CGPoint(x: 0, y: 1) |
| 147 | + #expect(ptWithNan.y.isNaN) |
| 148 | + |
| 149 | + // Extreme magnitude causing small additions to "snap back" to the value before addition |
| 150 | + let large = CGFloat.greatestFiniteMagnitude |
| 151 | + let ptLarge = CGPoint(x: large, y: large).offset(dx: 1, dy: -1) |
| 152 | + #expect(large == ptLarge.x) |
| 153 | + #expect(large == ptLarge.y) |
| 154 | + |
| 155 | + // Very small values (close to zero) |
| 156 | + let tiny = CGFloat(1e-300) |
| 157 | + #expect(CGPoint.zero.offset(dx: tiny, dy: -tiny) == CGPoint(x: tiny, y: -tiny)) |
| 158 | + } |
| 159 | + } |
72 | 160 | } |
0 commit comments