<commit_before>//
//  Path+DataTests.swift
//  BezierKit
//
//  Created by <NAME> on 3/13/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import XCTest
@testable import BezierKit

private struct CGPathElementRecord: Equatable {
    var type: CGPathElementType
    var pointsArray: [CGPoint]
    init(_ pathElement: CGPathElement) {
        self.type = pathElement.type
        let count = { () -> Int in
            switch pathElement.type {
            case .moveToPoint:
                return 1
            case .addLineToPoint:
                return 1
            case .addQuadCurveToPoint:
                return 2
            case .addCurveToPoint:
                return 3
            case .closeSubpath:
                return 0
            @unknown default:
                fatalError("unexpected unknown path element type: \(pathElement.type)")
            }
        }()
        self.pointsArray = [CGPoint](UnsafeBufferPointer(start: pathElement.points, count: count))
    }
}

fileprivate extension CGPath {
    private class ElementGetterContext {
        var elements: [CGPathElementRecord] = []
    }
    var elements: [CGPathElementRecord] {
        func elementGetterApplierFunction(_ info: UnsafeMutableRawPointer?, _ element: UnsafePointer<CGPathElement>) {
            let context = info!.assumingMemoryBound(to: ElementGetterContext.self).pointee
            context.elements.append(CGPathElementRecord(element.pointee))
        }
        var elementGetterContext = ElementGetterContext()
        self.apply(info: &elementGetterContext, function: elementGetterApplierFunction)
        return elementGetterContext.elements
    }
}

class PathDataTests: XCTestCase {

    private func pathHasEqualElementsToCGPath(_ path1: Path, _ path2: CGPath) -> Bool {
        return cgPathsHaveEqualCGPathElements(path1.cgPath, path2)
    }

    private func cgPathsHaveEqualCGPathElements(_ path1: CGPath, _ path2: CGPath) -> Bool {
        // checks that the CGPathElements that make up the paths are exactly equal
        // unfortunately we cannot just check path1 == path2 because CGPath.isRect can differ even if the underlying data is the same
        let pathElements1 = path1.elements
        let pathElements2 = path2.elements
        return pathElements1 == pathElements2
    }

    func testTooling() {
        let rect = CGRect(x: 1, y: 1, width: 2, height: 3)
        let rectCGPath = CGPath(rect: rect, transform: nil)
        let ellipseCGPath = CGPath(ellipseIn: rect, transform: nil)
        let emptyPath = CGMutablePath()
        let quadPath1 = { () -> CGPath in
            let cgPath = CGMutablePath()
            cgPath.move(to: CGPoint(x: 1, y: 1))
            cgPath.addQuadCurve(to: CGPoint(x: 3, y: 1), control: CGPoint(x: 3, y: 4))
            return cgPath
        }()
        let quadPath2 = { () -> CGPath in
            let cgPath = CGMutablePath()
            cgPath.move(to: CGPoint(x: 1, y: 1))
            cgPath.addQuadCurve(to: CGPoint(x: 3, y: 1), control: CGPoint(x: 3, y: 5))
            return cgPath
        }()
        XCTAssertTrue(cgPathsHaveEqualCGPathElements(emptyPath, emptyPath))
        XCTAssertFalse(cgPathsHaveEqualCGPathElements(rectCGPath, emptyPath))
        XCTAssertFalse(cgPathsHaveEqualCGPathElements(rectCGPath, ellipseCGPath))
        XCTAssertFalse(cgPathsHaveEqualCGPathElements(quadPath1, quadPath2))
        XCTAssertTrue(cgPathsHaveEqualCGPathElements(quadPath1, quadPath1))
        XCTAssertTrue(cgPathsHaveEqualCGPathElements(rectCGPath, rectCGPath))
        XCTAssertTrue(cgPathsHaveEqualCGPathElements(ellipseCGPath, ellipseCGPath))
    }

    func testEmpty() {
        let emptyCGPath = CGMutablePath()
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(), emptyCGPath))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: emptyCGPath), emptyCGPath))
    }

    func testRectangle() {
        let rectCGPath = CGPath(rect: CGRect(x: 1, y: 1, width: 2, height: 3), transform: nil)
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: rectCGPath), rectCGPath))
    }

    func testSingleOpenPath() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        cgPath.addCurve(to: CGPoint(x: 4, y: 5), control1: CGPoint(x: 5, y: 5), control2: CGPoint(x: 6, y: 4))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testSingleClosedPathClosePath() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        cgPath.addLine(to: CGPoint(x: 4, y: 4))
        cgPath.addLine(to: CGPoint(x: 4, y: 5))
        cgPath.addLine(to: CGPoint(x: 3, y: 5))
        cgPath.closeSubpath()
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testMultipleOpenPaths() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        cgPath.addLine(to: CGPoint(x: 4, y: 5))
        cgPath.move(to: CGPoint(x: 6, y: 4))
        cgPath.addLine(to: CGPoint(x: 7, y: 5))
        cgPath.move(to: CGPoint(x: 9, y: 4))
        cgPath.addLine(to: CGPoint(x: 10, y: 5))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testSinglePointMoveTo() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testSinglePointMoveToCloseSubpath() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        let beforeClosing = Path(cgPath: cgPath)
        cgPath.closeSubpath()
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), beforeClosing.cgPath))
    }

    func testMultipleSinglePoints() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 1, y: 2))
        cgPath.move(to: CGPoint(x: 2, y: 3))
        cgPath.move(to: CGPoint(x: 3, y: 4))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
        cgPath.addLine(to: CGPoint(x: 4, y: 5))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testMultipleComponentsNoMoveto() {
        // ensure that if the cgPath starts a new component without a move(to:) command that we still work properly
        let cgPath = CGMutablePath()
        let firstComponentPoints = [CGPoint(x: 0, y: 0),
                                    CGPoint(x: 1, y: 0),
                                    CGPoint(x: 1, y: 1),
                                    CGPoint(x: 0, y: 1),
                                    CGPoint(x: 0, y: 0)]
        let secondComponentPoint = CGPoint(x: -1, y: 0)
        cgPath.addLines(between: firstComponentPoints)<commit_msg>fixed a problem with code coverage where we had stopped actually converting to and from data in the tests.<commit_after>//
//  Path+DataTests.swift
//  BezierKit
//
//  Created by <NAME> on 3/13/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import XCTest
@testable import BezierKit

private struct CGPathElementRecord: Equatable {
    var type: CGPathElementType
    var pointsArray: [CGPoint]
    init(_ pathElement: CGPathElement) {
        self.type = pathElement.type
        let count = { () -> Int in
            switch pathElement.type {
            case .moveToPoint:
                return 1
            case .addLineToPoint:
                return 1
            case .addQuadCurveToPoint:
                return 2
            case .addCurveToPoint:
                return 3
            case .closeSubpath:
                return 0
            @unknown default:
                fatalError("unexpected unknown path element type: \(pathElement.type)")
            }
        }()
        self.pointsArray = [CGPoint](UnsafeBufferPointer(start: pathElement.points, count: count))
    }
}

fileprivate extension CGPath {
    private class ElementGetterContext {
        var elements: [CGPathElementRecord] = []
    }
    var elements: [CGPathElementRecord] {
        func elementGetterApplierFunction(_ info: UnsafeMutableRawPointer?, _ element: UnsafePointer<CGPathElement>) {
            let context = info!.assumingMemoryBound(to: ElementGetterContext.self).pointee
            context.elements.append(CGPathElementRecord(element.pointee))
        }
        var elementGetterContext = ElementGetterContext()
        self.apply(info: &elementGetterContext, function: elementGetterApplierFunction)
        return elementGetterContext.elements
    }
}

class PathDataTests: XCTestCase {

    private func pathHasEqualElementsToCGPath(_ path1: Path, _ path2: CGPath) -> Bool {
        return cgPathsHaveEqualCGPathElements(Path(data: path1.data)!.cgPath, path2)
    }

    private func cgPathsHaveEqualCGPathElements(_ path1: CGPath, _ path2: CGPath) -> Bool {
        // checks that the CGPathElements that make up the paths are exactly equal
        // unfortunately we cannot just check path1 == path2 because CGPath.isRect can differ even if the underlying data is the same
        let pathElements1 = path1.elements
        let pathElements2 = path2.elements
        return pathElements1 == pathElements2
    }

    func testTooling() {
        let rect = CGRect(x: 1, y: 1, width: 2, height: 3)
        let rectCGPath = CGPath(rect: rect, transform: nil)
        let ellipseCGPath = CGPath(ellipseIn: rect, transform: nil)
        let emptyPath = CGMutablePath()
        let quadPath1 = { () -> CGPath in
            let cgPath = CGMutablePath()
            cgPath.move(to: CGPoint(x: 1, y: 1))
            cgPath.addQuadCurve(to: CGPoint(x: 3, y: 1), control: CGPoint(x: 3, y: 4))
            return cgPath
        }()
        let quadPath2 = { () -> CGPath in
            let cgPath = CGMutablePath()
            cgPath.move(to: CGPoint(x: 1, y: 1))
            cgPath.addQuadCurve(to: CGPoint(x: 3, y: 1), control: CGPoint(x: 3, y: 5))
            return cgPath
        }()
        XCTAssertTrue(cgPathsHaveEqualCGPathElements(emptyPath, emptyPath))
        XCTAssertFalse(cgPathsHaveEqualCGPathElements(rectCGPath, emptyPath))
        XCTAssertFalse(cgPathsHaveEqualCGPathElements(rectCGPath, ellipseCGPath))
        XCTAssertFalse(cgPathsHaveEqualCGPathElements(quadPath1, quadPath2))
        XCTAssertTrue(cgPathsHaveEqualCGPathElements(quadPath1, quadPath1))
        XCTAssertTrue(cgPathsHaveEqualCGPathElements(rectCGPath, rectCGPath))
        XCTAssertTrue(cgPathsHaveEqualCGPathElements(ellipseCGPath, ellipseCGPath))
    }

    func testEmpty() {
        let emptyCGPath = CGMutablePath()
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(), emptyCGPath))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: emptyCGPath), emptyCGPath))
    }

    func testRectangle() {
        let rectCGPath = CGPath(rect: CGRect(x: 1, y: 1, width: 2, height: 3), transform: nil)
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: rectCGPath), rectCGPath))
    }

    func testSingleOpenPath() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        cgPath.addCurve(to: CGPoint(x: 4, y: 5), control1: CGPoint(x: 5, y: 5), control2: CGPoint(x: 6, y: 4))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testSingleClosedPathClosePath() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        cgPath.addLine(to: CGPoint(x: 4, y: 4))
        cgPath.addLine(to: CGPoint(x: 4, y: 5))
        cgPath.addLine(to: CGPoint(x: 3, y: 5))
        cgPath.closeSubpath()
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testMultipleOpenPaths() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        cgPath.addLine(to: CGPoint(x: 4, y: 5))
        cgPath.move(to: CGPoint(x: 6, y: 4))
        cgPath.addLine(to: CGPoint(x: 7, y: 5))
        cgPath.move(to: CGPoint(x: 9, y: 4))
        cgPath.addLine(to: CGPoint(x: 10, y: 5))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testSinglePointMoveTo() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testSinglePointMoveToCloseSubpath() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 3, y: 4))
        let beforeClosing = Path(cgPath: cgPath)
        cgPath.closeSubpath()
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), beforeClosing.cgPath))
    }

    func testMultipleSinglePoints() {
        let cgPath = CGMutablePath()
        cgPath.move(to: CGPoint(x: 1, y: 2))
        cgPath.move(to: CGPoint(x: 2, y: 3))
        cgPath.move(to: CGPoint(x: 3, y: 4))

        let what = Path(cgPath: cgPath)

        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
        cgPath.addLine(to: CGPoint(x: 4, y: 5))
        XCTAssertTrue(pathHasEqualElementsToCGPath(Path(cgPath: cgPath), cgPath))
    }

    func testMultipleComponentsNoMoveto() {
        // ensure that if the cgPath starts a new component without a move(to:) command that we still work properly
        let cgPath = CGMutablePath()
        let firstComponentPoints = [CGPoint(x: 0, y: 0),
                                    CGPoint(x: 1, y: 0),
                                    CGPoint(x: 1, y: 1),
                                    CGPoint(x: 0, y: 1),
                                    CGPoint(x: 0, y: 0)]
        let secondComponentPoint = CGPoint(x: -1, y: 0)
        cgPath.addLines(between: firstComponentPoints)
{'fraction_non_alphanumeric': 0.1033904484141451, 'fraction_numerical': 0.015895005468465184, 'mean_word_length': 2.6295316221222547, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2641274', 'n_tokens_mistral': 4680, 'n_tokens_neox': 4298, 'n_words': 1014}