Add AtomList tests
This commit is contained in:
@@ -1,624 +0,0 @@
|
|||||||
import XCTest
|
|
||||||
@testable import SwiftUIMath
|
|
||||||
|
|
||||||
//
|
|
||||||
// MathRenderSwiftTests.swift
|
|
||||||
// MathRenderSwiftTests
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2023-01-02.
|
|
||||||
//
|
|
||||||
|
|
||||||
final class MTMathListTests: XCTestCase {
|
|
||||||
|
|
||||||
func testSubScript() throws {
|
|
||||||
let str = "-52x^{13+y}_{15-} + (-12.3 *)\\frac{-12}{15.2}"
|
|
||||||
let list = MTMathListBuilder.build(fromString: str)!
|
|
||||||
let finalized = list.finalized
|
|
||||||
try self.checkListContents(finalized)
|
|
||||||
// refinalizing a finalized list should not cause any more changes
|
|
||||||
try self.checkListContents(finalized.finalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkListContents(_ finalized:MTMathList) throws {
|
|
||||||
// check
|
|
||||||
XCTAssertEqual((finalized.atoms.count), 10, "Num atoms");
|
|
||||||
var atom = finalized.atoms[0];
|
|
||||||
XCTAssertEqual(atom.type, .unaryOperator, "Atom 0");
|
|
||||||
XCTAssertEqual(atom.nucleus, "−", "Atom 0 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(0, 1)), "Range");
|
|
||||||
atom = finalized.atoms[1];
|
|
||||||
XCTAssertEqual(atom.type, .number, "Atom 1");
|
|
||||||
XCTAssertEqual(atom.nucleus, "52", "Atom 1 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(1, 2)), "Range");
|
|
||||||
atom = finalized.atoms[2];
|
|
||||||
XCTAssertEqual(atom.type, .variable, "Atom 2");
|
|
||||||
XCTAssertEqual(atom.nucleus, "x", "Atom 2 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(3, 1)), "Range");
|
|
||||||
|
|
||||||
let superScr = atom.superScript!
|
|
||||||
XCTAssertEqual((superScr.atoms.count), 3, "Super script");
|
|
||||||
atom = superScr.atoms[0];
|
|
||||||
XCTAssertEqual(atom.type, .number, "Super Atom 0");
|
|
||||||
XCTAssertEqual(atom.nucleus, "13", "Super Atom 0 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(0, 2)), "Range");
|
|
||||||
atom = superScr.atoms[1];
|
|
||||||
XCTAssertEqual(atom.type, .binaryOperator, "Super Atom 1");
|
|
||||||
XCTAssertEqual(atom.nucleus, "+", "Super Atom 1 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(2, 1)), "Range");
|
|
||||||
atom = superScr.atoms[2];
|
|
||||||
XCTAssertEqual(atom.type, .variable, "Super Atom 2");
|
|
||||||
XCTAssertEqual(atom.nucleus, "y", "Super Atom 2 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(3, 1)), "Range");
|
|
||||||
|
|
||||||
atom = finalized.atoms[2];
|
|
||||||
let subScr = atom.subScript!
|
|
||||||
XCTAssertEqual((subScr.atoms.count), 2, "Sub script");
|
|
||||||
atom = subScr.atoms[0];
|
|
||||||
XCTAssertEqual(atom.type, .number, "Sub Atom 0");
|
|
||||||
XCTAssertEqual(atom.nucleus, "15", "Sub Atom 0 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(0, 2)), "Range");
|
|
||||||
atom = subScr.atoms[1];
|
|
||||||
XCTAssertEqual(atom.type, .unaryOperator, "Sub Atom 1");
|
|
||||||
XCTAssertEqual(atom.nucleus, "−", "Sub Atom 1 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(2, 1)), "Range");
|
|
||||||
|
|
||||||
atom = finalized.atoms[3];
|
|
||||||
XCTAssertEqual(atom.type, .binaryOperator, "Atom 3");
|
|
||||||
XCTAssertEqual(atom.nucleus, "+", "Atom 3 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(4, 1)), "Range");
|
|
||||||
atom = finalized.atoms[4];
|
|
||||||
XCTAssertEqual(atom.type, .open, "Atom 4");
|
|
||||||
XCTAssertEqual(atom.nucleus, "(", "Atom 4 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(5, 1)), "Range");
|
|
||||||
atom = finalized.atoms[5];
|
|
||||||
XCTAssertEqual(atom.type, .unaryOperator, "Atom 5");
|
|
||||||
XCTAssertEqual(atom.nucleus, "−", "Atom 5 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(6, 1)), "Range");
|
|
||||||
atom = finalized.atoms[6];
|
|
||||||
XCTAssertEqual(atom.type, .number, "Atom 6");
|
|
||||||
XCTAssertEqual(atom.nucleus, "12.3", "Atom 6 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(7, 4)), "Range");
|
|
||||||
atom = finalized.atoms[7];
|
|
||||||
XCTAssertEqual(atom.type, .unaryOperator, "Atom 7");
|
|
||||||
XCTAssertEqual(atom.nucleus, "*", "Atom 7 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(11, 1)), "Range");
|
|
||||||
atom = finalized.atoms[8];
|
|
||||||
XCTAssertEqual(atom.type, .close, "Atom 8");
|
|
||||||
XCTAssertEqual(atom.nucleus, ")", "Atom 8 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(12, 1)), "Range");
|
|
||||||
|
|
||||||
let frac = finalized.atoms[9] as! MTFraction
|
|
||||||
XCTAssertEqual(frac.type, .fraction, "Atom 9");
|
|
||||||
XCTAssertEqual(frac.nucleus, "", "Atom 9 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(frac.indexRange, NSMakeRange(13, 1)), "Range");
|
|
||||||
|
|
||||||
let numer = frac.numerator!
|
|
||||||
XCTAssertNotNil(numer, "Numerator");
|
|
||||||
XCTAssertEqual((numer.atoms.count), 2, "Numer script");
|
|
||||||
atom = numer.atoms[0];
|
|
||||||
XCTAssertEqual(atom.type, .unaryOperator, "Numer Atom 0");
|
|
||||||
XCTAssertEqual(atom.nucleus, "−", "Numer Atom 0 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(0, 1)), "Range");
|
|
||||||
atom = numer.atoms[1];
|
|
||||||
XCTAssertEqual(atom.type, .number, "Numer Atom 1");
|
|
||||||
XCTAssertEqual(atom.nucleus, "12", "Numer Atom 1 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(1, 2)), "Range");
|
|
||||||
|
|
||||||
|
|
||||||
let denom = frac.denominator!
|
|
||||||
XCTAssertNotNil(denom, "Denominator");
|
|
||||||
XCTAssertEqual((denom.atoms.count), 1, "Denom script");
|
|
||||||
atom = denom.atoms[0];
|
|
||||||
XCTAssertEqual(atom.type, .number, "Denom Atom 0");
|
|
||||||
XCTAssertEqual(atom.nucleus, "15.2", "Denom Atom 0 value");
|
|
||||||
XCTAssertTrue(NSEqualRanges(atom.indexRange, NSMakeRange(0, 4)), "Range");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAdd() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
XCTAssertEqual(list.atoms.count, 0);
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
list.add(atom)
|
|
||||||
XCTAssertEqual(list.atoms.count, 1);
|
|
||||||
XCTAssertEqual(list.atoms[0], atom);
|
|
||||||
let atom2 = MTMathAtomFactory.placeholder()
|
|
||||||
list.add(atom2);
|
|
||||||
XCTAssertEqual(list.atoms.count, 2);
|
|
||||||
XCTAssertEqual(list.atoms[0], atom);
|
|
||||||
XCTAssertEqual(list.atoms[1], atom2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private var options : XCTExpectedFailure.Options {
|
|
||||||
let op = XCTExpectedFailure.Options()
|
|
||||||
op.isStrict = true
|
|
||||||
return op
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAddErrors() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
var atom : MTMathAtom? = nil
|
|
||||||
list.add(atom)
|
|
||||||
atom = MTMathAtom(type: .boundary, value: "")
|
|
||||||
XCTExpectFailure("Test adding an illegal atom", options:options) {
|
|
||||||
XCTAssertThrowsError(list.add(atom))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testInsert() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
XCTAssertEqual(list.atoms.count, 0);
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
list.insert(atom, at: 0)
|
|
||||||
XCTAssertEqual(list.atoms.count, 1);
|
|
||||||
XCTAssertEqual(list.atoms[0], atom);
|
|
||||||
let atom2 = MTMathAtomFactory.placeholder()
|
|
||||||
list.insert(atom2, at: 0)
|
|
||||||
XCTAssertEqual(list.atoms.count, 2);
|
|
||||||
XCTAssertEqual(list.atoms[0], atom2);
|
|
||||||
XCTAssertEqual(list.atoms[1], atom);
|
|
||||||
let atom3 = MTMathAtomFactory.placeholder()
|
|
||||||
list.insert(atom3, at: 2)
|
|
||||||
XCTAssertEqual(list.atoms.count, 3);
|
|
||||||
XCTAssertEqual(list.atoms[0], atom2);
|
|
||||||
XCTAssertEqual(list.atoms[1], atom);
|
|
||||||
XCTAssertEqual(list.atoms[2], atom3);
|
|
||||||
}
|
|
||||||
|
|
||||||
func testInsertErrors() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
var atom : MTMathAtom? = nil
|
|
||||||
list.insert(atom, at: 0)
|
|
||||||
atom = MTMathAtom(type: .boundary, value:"")
|
|
||||||
XCTExpectFailure("Test adding an illegal atom", options:options) {
|
|
||||||
XCTAssertThrowsError(list.insert(atom, at:0))
|
|
||||||
}
|
|
||||||
atom = MTMathAtomFactory.placeholder()
|
|
||||||
list.insert(atom, at:1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAppend() throws {
|
|
||||||
let list1 = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.placeholder()
|
|
||||||
let atom3 = MTMathAtomFactory.placeholder()
|
|
||||||
list1.add(atom)
|
|
||||||
list1.add(atom2)
|
|
||||||
list1.add(atom3)
|
|
||||||
|
|
||||||
let list2 = MTMathList()
|
|
||||||
let atom5 = MTMathAtomFactory.times()
|
|
||||||
let atom6 = MTMathAtomFactory.divide()
|
|
||||||
list2.add(atom5)
|
|
||||||
list2.add(atom6)
|
|
||||||
|
|
||||||
XCTAssertEqual(list1.atoms.count, 3);
|
|
||||||
XCTAssertEqual(list2.atoms.count, 2);
|
|
||||||
|
|
||||||
list1.append(list2)
|
|
||||||
XCTAssertEqual(list1.atoms.count, 5);
|
|
||||||
XCTAssertEqual(list1.atoms[3], atom5);
|
|
||||||
XCTAssertEqual(list1.atoms[4], atom6);
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRemoveLast() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
list.add(atom)
|
|
||||||
XCTAssertEqual(list.atoms.count, 1);
|
|
||||||
list.removeLastAtom()
|
|
||||||
XCTAssertEqual(list.atoms.count, 0);
|
|
||||||
// Removing from empty list.
|
|
||||||
list.removeLastAtom()
|
|
||||||
XCTAssertEqual(list.atoms.count, 0);
|
|
||||||
let atom2 = MTMathAtomFactory.placeholder()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
XCTAssertEqual(list.atoms.count, 2);
|
|
||||||
list.removeLastAtom()
|
|
||||||
XCTAssertEqual(list.atoms.count, 1);
|
|
||||||
XCTAssertEqual(list.atoms[0], atom);
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRemoveAtomAtIndex() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.placeholder()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
XCTAssertEqual(list.atoms.count, 2);
|
|
||||||
list.removeAtom(at:0)
|
|
||||||
XCTAssertEqual(list.atoms.count, 1);
|
|
||||||
XCTAssertEqual(list.atoms[0], atom2);
|
|
||||||
|
|
||||||
// Index out of range
|
|
||||||
XCTExpectFailure("Test removing an out-of-index cell", options: options) {
|
|
||||||
XCTAssertThrowsError(list.removeAtom(at:2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRemoveAtomsInRange() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.placeholder()
|
|
||||||
let atom3 = MTMathAtomFactory.placeholder()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
XCTAssertEqual(list.atoms.count, 3)
|
|
||||||
list.removeAtoms(in: 1...2)
|
|
||||||
XCTAssertEqual(list.atoms.count, 1);
|
|
||||||
XCTAssertEqual(list.atoms[0], atom);
|
|
||||||
|
|
||||||
// Index out of range
|
|
||||||
XCTExpectFailure("Test removing an out-of-bounds range", options: options) {
|
|
||||||
XCTAssertThrowsError(list.removeAtoms(in: 1...3))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func MTAssertEqual(test, expression1, expression2, ...) \
|
|
||||||
// _XCTPrimitiveAssertEqual(test, expression1, @#expression1, expression2, @#expression2, __VA_ARGS__)
|
|
||||||
//
|
|
||||||
// func MTAssertNotEqual(test, expression1, expression2, ...) \
|
|
||||||
// _XCTPrimitiveAssertNotEqual(test, expression1, @#expression1, expression2, @#expression2, __VA_ARGS__)
|
|
||||||
|
|
||||||
func checkAtomCopy(_ copy:MTMathAtom?, original:MTMathAtom?, forTest test:String) throws {
|
|
||||||
guard let copy = copy, let original = original else { return }
|
|
||||||
XCTAssertEqual(copy.type, original.type, test)
|
|
||||||
XCTAssertEqual(copy.nucleus, original.nucleus, test)
|
|
||||||
// Should be different objects with the same content
|
|
||||||
XCTAssertNotEqual(copy, original, test)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkListCopy(_ copy:MTMathList?, original:MTMathList?, forTest test:String) throws {
|
|
||||||
guard let copy = copy, let original = original else { return }
|
|
||||||
XCTAssertEqual(copy.atoms.count, original.atoms.count, test)
|
|
||||||
for (i, copyAtom) in copy.atoms.enumerated() {
|
|
||||||
let origAtom = original.atoms[i];
|
|
||||||
try self.checkAtomCopy(copyAtom, original:origAtom, forTest:test)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopy() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let list2 = MTMathList(list)
|
|
||||||
try checkListCopy(list2, original:list, forTest:self.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAtomInit() throws {
|
|
||||||
var atom = MTMathAtom(type: .open, value: "(")
|
|
||||||
XCTAssertEqual(atom.nucleus, "(")
|
|
||||||
XCTAssertEqual(atom.type, .open)
|
|
||||||
|
|
||||||
atom = MTMathAtom(type: .radical, value:"(")
|
|
||||||
XCTAssertEqual(atom.nucleus, "");
|
|
||||||
XCTAssertEqual(atom.type, .radical);
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAtomScripts() throws {
|
|
||||||
var atom = MTMathAtom(type: .open, value:"(")
|
|
||||||
XCTAssertTrue(atom.isScriptAllowed())
|
|
||||||
atom.subScript = MTMathList()
|
|
||||||
XCTAssertNotNil(atom.subScript);
|
|
||||||
atom.superScript = MTMathList()
|
|
||||||
XCTAssertNotNil(atom.superScript);
|
|
||||||
|
|
||||||
atom = MTMathAtom(type: .boundary, value:"(")
|
|
||||||
XCTAssertFalse(atom.isScriptAllowed());
|
|
||||||
// Can set to nil
|
|
||||||
atom.subScript = nil;
|
|
||||||
XCTAssertNil(atom.subScript);
|
|
||||||
atom.superScript = nil;
|
|
||||||
XCTAssertNil(atom.superScript);
|
|
||||||
// Can't set to value
|
|
||||||
let list = MTMathList()
|
|
||||||
|
|
||||||
XCTExpectFailure("No sub/super-script on boundary atoms", options: options) {
|
|
||||||
XCTAssertThrowsError(atom.subScript = list)
|
|
||||||
XCTAssertThrowsError(atom.superScript = list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAtomCopy() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom1 = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom1)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let list2 = MTMathList()
|
|
||||||
list2.add(atom3)
|
|
||||||
list2.add(atom2)
|
|
||||||
|
|
||||||
let atom = MTMathAtom(type: .open, value:"(")
|
|
||||||
atom.subScript = list;
|
|
||||||
atom.superScript = list2;
|
|
||||||
let copy : MTMathAtom = atom.copy()
|
|
||||||
|
|
||||||
try checkAtomCopy(copy, original:atom, forTest:self.description)
|
|
||||||
try checkListCopy(copy.superScript, original:atom.superScript, forTest:self.description)
|
|
||||||
try checkListCopy(copy.subScript, original:atom.subScript, forTest:self.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopyFraction() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let list2 = MTMathList()
|
|
||||||
list2.add(atom3)
|
|
||||||
list2.add(atom2)
|
|
||||||
|
|
||||||
let frac = MTFraction(hasRule: false)
|
|
||||||
XCTAssertEqual(frac.type, .fraction);
|
|
||||||
frac.numerator = list;
|
|
||||||
frac.denominator = list2;
|
|
||||||
frac.leftDelimiter = "a";
|
|
||||||
frac.rightDelimiter = "b";
|
|
||||||
|
|
||||||
let copy = MTFraction(frac)
|
|
||||||
try checkAtomCopy(copy, original:frac, forTest:self.description)
|
|
||||||
try checkListCopy(copy.numerator, original:frac.numerator, forTest:self.description)
|
|
||||||
try checkListCopy(copy.denominator, original:frac.denominator, forTest:self.description)
|
|
||||||
XCTAssertFalse(copy.hasRule)
|
|
||||||
XCTAssertEqual(copy.leftDelimiter, "a");
|
|
||||||
XCTAssertEqual(copy.rightDelimiter, "b");
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopyRadical() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let list2 = MTMathList()
|
|
||||||
list2.add(atom3)
|
|
||||||
list2.add(atom2)
|
|
||||||
|
|
||||||
let rad = MTRadical()
|
|
||||||
XCTAssertEqual(rad.type, .radical)
|
|
||||||
rad.radicand = list;
|
|
||||||
rad.degree = list2;
|
|
||||||
|
|
||||||
let copy = MTRadical(rad)
|
|
||||||
try checkAtomCopy(copy, original:rad, forTest:self.description)
|
|
||||||
try checkListCopy(copy.radicand, original:rad.radicand ,forTest:self.description)
|
|
||||||
try checkListCopy(copy.degree, original:rad.degree, forTest:self.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopyLargeOperator() throws {
|
|
||||||
let lg = MTLargeOperator(value: "lim", limits:true)
|
|
||||||
XCTAssertEqual(lg.type, .largeOperator);
|
|
||||||
XCTAssertTrue(lg.limits);
|
|
||||||
|
|
||||||
let copy = MTLargeOperator(lg)
|
|
||||||
try checkAtomCopy(copy, original:lg, forTest:self.description)
|
|
||||||
XCTAssertEqual(copy.limits, lg.limits);
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopyInner() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let inner = MTInner()
|
|
||||||
inner.innerList = list;
|
|
||||||
inner.leftBoundary = MTMathAtom(type: .boundary, value: "(")
|
|
||||||
inner.rightBoundary = MTMathAtom(type: .boundary, value:")")
|
|
||||||
XCTAssertEqual(inner.type, .inner);
|
|
||||||
|
|
||||||
let copy = MTInner(inner)
|
|
||||||
try checkAtomCopy(copy, original:inner, forTest:self.description)
|
|
||||||
try checkListCopy(copy.innerList, original:inner.innerList, forTest:self.description)
|
|
||||||
try checkAtomCopy(copy.leftBoundary!, original:inner.leftBoundary, forTest:self.description)
|
|
||||||
try checkAtomCopy(copy.rightBoundary, original:inner.rightBoundary, forTest:self.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSetInnerBoundary() throws {
|
|
||||||
let inner = MTInner()
|
|
||||||
|
|
||||||
// Can set non-nil
|
|
||||||
inner.leftBoundary = MTMathAtom(type: .boundary, value:"(")
|
|
||||||
inner.rightBoundary = MTMathAtom(type: .boundary, value:")")
|
|
||||||
XCTAssertNotNil(inner.leftBoundary);
|
|
||||||
XCTAssertNotNil(inner.rightBoundary);
|
|
||||||
// Can set nil
|
|
||||||
inner.leftBoundary = nil;
|
|
||||||
inner.rightBoundary = nil;
|
|
||||||
XCTAssertNil(inner.leftBoundary);
|
|
||||||
XCTAssertNil(inner.rightBoundary);
|
|
||||||
// Can't set non boundary
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
XCTExpectFailure("Setting illegal boundary atoms", options: options) {
|
|
||||||
XCTAssertThrowsError(inner.leftBoundary = atom);
|
|
||||||
XCTAssertThrowsError(inner.rightBoundary = atom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopyOverline() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let over = MTOverLine()
|
|
||||||
XCTAssertEqual(over.type, .overline);
|
|
||||||
over.innerList = list;
|
|
||||||
|
|
||||||
let copy = MTOverLine(over)
|
|
||||||
try checkAtomCopy(copy, original:over, forTest:self.description)
|
|
||||||
try checkListCopy(copy.innerList, original:over.innerList, forTest:self.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopyUnderline() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let under = MTUnderLine()
|
|
||||||
XCTAssertEqual(under.type, .underline);
|
|
||||||
under.innerList = list;
|
|
||||||
|
|
||||||
let copy = MTUnderLine(under)
|
|
||||||
try checkAtomCopy(copy, original:under, forTest:self.description)
|
|
||||||
try checkListCopy(copy.innerList, original:under.innerList, forTest:self.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopyAcccent() throws {
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let accent = MTAccent(value: "^")
|
|
||||||
XCTAssertEqual(accent.type, .accent);
|
|
||||||
accent.innerList = list;
|
|
||||||
|
|
||||||
let copy = MTAccent(accent)
|
|
||||||
try checkAtomCopy(copy, original:accent, forTest:self.description)
|
|
||||||
try checkListCopy(copy.innerList ,original:accent.innerList, forTest:self.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopySpace() throws {
|
|
||||||
let space = MTMathSpace(space: 3)
|
|
||||||
XCTAssertEqual(space.type, .space);
|
|
||||||
|
|
||||||
let copy = MTMathSpace(space)
|
|
||||||
try checkAtomCopy(copy, original:space, forTest:self.description)
|
|
||||||
XCTAssertEqual(space.space, copy.space);
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopyStyle() throws {
|
|
||||||
let style = MTMathStyle(style: .script)
|
|
||||||
XCTAssertEqual(style.type, .style);
|
|
||||||
|
|
||||||
let copy = MTMathStyle(style)
|
|
||||||
try checkAtomCopy(copy, original:style, forTest:self.description)
|
|
||||||
XCTAssertEqual(style.style, copy.style);
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCreateMathTable() throws {
|
|
||||||
let table = MTMathTable()
|
|
||||||
XCTAssertEqual(table.type, .table);
|
|
||||||
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let list2 = MTMathList()
|
|
||||||
list2.add(atom3)
|
|
||||||
list2.add(atom2)
|
|
||||||
|
|
||||||
table.set(cell: list, forRow:3, column:2)
|
|
||||||
table.set(cell: list2, forRow:1, column:0)
|
|
||||||
|
|
||||||
table.set(alignment: .left, forColumn: 2)
|
|
||||||
table.set(alignment: .right, forColumn:1)
|
|
||||||
|
|
||||||
// Verify that everything is created correctly
|
|
||||||
XCTAssertEqual(table.cells.count, 4); // 4 rows
|
|
||||||
XCTAssertNotNil(table.cells[0]);
|
|
||||||
XCTAssertEqual(table.cells[0].count, 0); // 0 elements in row 0
|
|
||||||
XCTAssertEqual(table.cells[1].count, 1); // 1 element in row 1
|
|
||||||
XCTAssertNotNil(table.cells[2]);
|
|
||||||
XCTAssertEqual(table.cells[2].count, 0);
|
|
||||||
XCTAssertEqual(table.cells[3].count, 3);
|
|
||||||
|
|
||||||
// Verify the elements in the rows
|
|
||||||
XCTAssertEqual(table.cells[1][0].atoms.count, 2);
|
|
||||||
XCTAssertEqual(table.cells[1][0], list2);
|
|
||||||
XCTAssertNotNil(table.cells[3][0]);
|
|
||||||
XCTAssertEqual(table.cells[3][0].atoms.count, 0);
|
|
||||||
|
|
||||||
XCTAssertNotNil(table.cells[3][0]);
|
|
||||||
XCTAssertEqual(table.cells[3][0].atoms.count, 0);
|
|
||||||
|
|
||||||
XCTAssertNotNil(table.cells[3][1]);
|
|
||||||
XCTAssertEqual(table.cells[3][1].atoms.count, 0);
|
|
||||||
|
|
||||||
XCTAssertEqual(table.cells[3][2], list);
|
|
||||||
|
|
||||||
XCTAssertEqual(table.numRows, 4);
|
|
||||||
XCTAssertEqual(table.numColumns, 3);
|
|
||||||
|
|
||||||
// Verify the alignments
|
|
||||||
XCTAssertEqual(table.alignments.count, 3);
|
|
||||||
XCTAssertEqual(table.alignments[0], .center);
|
|
||||||
XCTAssertEqual(table.alignments[1], .right);
|
|
||||||
XCTAssertEqual(table.alignments[2], .left);
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCopyMathTable() throws {
|
|
||||||
let table = MTMathTable()
|
|
||||||
XCTAssertEqual(table.type, .table);
|
|
||||||
|
|
||||||
let list = MTMathList()
|
|
||||||
let atom = MTMathAtomFactory.placeholder()
|
|
||||||
let atom2 = MTMathAtomFactory.times()
|
|
||||||
let atom3 = MTMathAtomFactory.divide()
|
|
||||||
list.add(atom)
|
|
||||||
list.add(atom2);
|
|
||||||
list.add(atom3)
|
|
||||||
|
|
||||||
let list2 = MTMathList()
|
|
||||||
list2.add(atom3)
|
|
||||||
list2.add(atom2)
|
|
||||||
|
|
||||||
table.set(cell:list, forRow:0, column:1)
|
|
||||||
table.set(cell:list2, forRow:0, column:2)
|
|
||||||
|
|
||||||
table.set(alignment: .left, forColumn:2)
|
|
||||||
table.set(alignment: .right, forColumn:1)
|
|
||||||
table.interRowAdditionalSpacing = 3;
|
|
||||||
table.interColumnSpacing = 10;
|
|
||||||
|
|
||||||
let copy = MTMathTable(table)
|
|
||||||
try checkAtomCopy(copy, original:table, forTest:self.description)
|
|
||||||
XCTAssertEqual(copy.interColumnSpacing, table.interColumnSpacing);
|
|
||||||
XCTAssertEqual(copy.interRowAdditionalSpacing, table.interRowAdditionalSpacing);
|
|
||||||
XCTAssertEqual(copy.alignments, table.alignments)
|
|
||||||
|
|
||||||
XCTAssertNotEqual(copy.cells, table.cells);
|
|
||||||
XCTAssertNotEqual(copy.cells[0], table.cells[0] );
|
|
||||||
XCTAssertEqual(copy.cells[0].count, table.cells[0].count);
|
|
||||||
XCTAssertEqual(copy.cells[0][0].atoms.count, 0);
|
|
||||||
XCTAssertNotEqual(copy.cells[0][0], table.cells[0][0]);
|
|
||||||
try checkListCopy(copy.cells[0][1], original:list, forTest:self.description)
|
|
||||||
try checkListCopy(copy.cells[0][2], original:list2, forTest:self.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
612
Tests/SwiftUIMathTests/Internal/Model/AtomListTests.swift
Normal file
612
Tests/SwiftUIMathTests/Internal/Model/AtomListTests.swift
Normal file
@@ -0,0 +1,612 @@
|
|||||||
|
import Foundation
|
||||||
|
import Testing
|
||||||
|
@testable import SwiftUIMath
|
||||||
|
|
||||||
|
@Suite
|
||||||
|
struct AtomListTests {
|
||||||
|
@Test
|
||||||
|
func parsesScriptsAndFinalizesAtomList() throws {
|
||||||
|
let input = "-52x^{13+y}_{15-} + (-12.3 *)\\frac{-12}{15.2}"
|
||||||
|
let list = try parseFinalizedAtomList(from: input)
|
||||||
|
|
||||||
|
try assertFinalizedAtomListContents(list)
|
||||||
|
// Re-finalizing should be stable.
|
||||||
|
try assertFinalizedAtomListContents(list.finalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func appendsAtomsInOrder() {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
#expect(list.atoms.isEmpty)
|
||||||
|
|
||||||
|
let first = Math.AtomFactory.placeholder()
|
||||||
|
list.append(first)
|
||||||
|
#expect(list.atoms.count == 1)
|
||||||
|
#expect(list.atoms[0] === first)
|
||||||
|
|
||||||
|
let second = Math.AtomFactory.placeholder()
|
||||||
|
list.append(second)
|
||||||
|
#expect(list.atoms.count == 2)
|
||||||
|
#expect(list.atoms[0] === first)
|
||||||
|
#expect(list.atoms[1] === second)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func insertsAtomsAtIndices() {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let first = Math.AtomFactory.placeholder()
|
||||||
|
list.insert(first, at: 0)
|
||||||
|
#expect(list.atoms.count == 1)
|
||||||
|
#expect(list.atoms[0] === first)
|
||||||
|
|
||||||
|
let second = Math.AtomFactory.placeholder()
|
||||||
|
list.insert(second, at: 0)
|
||||||
|
#expect(list.atoms.count == 2)
|
||||||
|
#expect(list.atoms[0] === second)
|
||||||
|
#expect(list.atoms[1] === first)
|
||||||
|
|
||||||
|
let third = Math.AtomFactory.placeholder()
|
||||||
|
list.insert(third, at: 2)
|
||||||
|
#expect(list.atoms.count == 3)
|
||||||
|
#expect(list.atoms[0] === second)
|
||||||
|
#expect(list.atoms[1] === first)
|
||||||
|
#expect(list.atoms[2] === third)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func appendsListContents() {
|
||||||
|
let list1 = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.placeholder()
|
||||||
|
let atom3 = Math.AtomFactory.placeholder()
|
||||||
|
list1.append(atom1)
|
||||||
|
list1.append(atom2)
|
||||||
|
list1.append(atom3)
|
||||||
|
|
||||||
|
let list2 = Math.AtomList()
|
||||||
|
let atom4 = Math.AtomFactory.times()
|
||||||
|
let atom5 = Math.AtomFactory.divide()
|
||||||
|
list2.append(atom4)
|
||||||
|
list2.append(atom5)
|
||||||
|
|
||||||
|
#expect(list1.atoms.count == 3)
|
||||||
|
#expect(list2.atoms.count == 2)
|
||||||
|
|
||||||
|
list1.append(contentsOf: list2)
|
||||||
|
#expect(list1.atoms.count == 5)
|
||||||
|
#expect(list1.atoms[3] === atom4)
|
||||||
|
#expect(list1.atoms[4] === atom5)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func removesLastAtom() {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom = Math.AtomFactory.placeholder()
|
||||||
|
list.append(atom)
|
||||||
|
|
||||||
|
#expect(list.atoms.count == 1)
|
||||||
|
list.removeLastAtomForTesting()
|
||||||
|
#expect(list.atoms.isEmpty)
|
||||||
|
|
||||||
|
list.removeLastAtomForTesting()
|
||||||
|
#expect(list.atoms.isEmpty)
|
||||||
|
|
||||||
|
let atom2 = Math.AtomFactory.placeholder()
|
||||||
|
list.append(atom)
|
||||||
|
list.append(atom2)
|
||||||
|
|
||||||
|
#expect(list.atoms.count == 2)
|
||||||
|
list.removeLastAtomForTesting()
|
||||||
|
#expect(list.atoms.count == 1)
|
||||||
|
#expect(list.atoms[0] === atom)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func removesAtomAtIndex() {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let first = Math.AtomFactory.placeholder()
|
||||||
|
let second = Math.AtomFactory.placeholder()
|
||||||
|
list.append(first)
|
||||||
|
list.append(second)
|
||||||
|
|
||||||
|
#expect(list.atoms.count == 2)
|
||||||
|
#expect(list.removeAtomForTesting(at: 0))
|
||||||
|
#expect(list.atoms.count == 1)
|
||||||
|
#expect(list.atoms[0] === second)
|
||||||
|
|
||||||
|
#expect(!list.removeAtomForTesting(at: 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func removesAtomsInRange() {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let first = Math.AtomFactory.placeholder()
|
||||||
|
let second = Math.AtomFactory.placeholder()
|
||||||
|
let third = Math.AtomFactory.placeholder()
|
||||||
|
list.append(first)
|
||||||
|
list.append(second)
|
||||||
|
list.append(third)
|
||||||
|
|
||||||
|
#expect(list.atoms.count == 3)
|
||||||
|
#expect(list.removeAtomsForTesting(in: 1...2))
|
||||||
|
#expect(list.atoms.count == 1)
|
||||||
|
#expect(list.atoms[0] === first)
|
||||||
|
|
||||||
|
#expect(!list.removeAtomsForTesting(in: 1...3))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesAtomListsWithDistinctAtoms() throws {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let copy = Math.AtomList(list)
|
||||||
|
try assertAtomListCopyMatches(copy, original: list, context: "atom list copy")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func initializesAtomWithCorrectNucleusAndType() {
|
||||||
|
var atom = Math.Atom(type: .open, value: "(")
|
||||||
|
#expect(atom.nucleus == "(")
|
||||||
|
#expect(atom.type == .open)
|
||||||
|
|
||||||
|
atom = Math.Atom(type: .radical, value: "(")
|
||||||
|
#expect(atom.nucleus.isEmpty)
|
||||||
|
#expect(atom.type == .radical)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func supportsScriptsWhenAllowed() {
|
||||||
|
var atom = Math.Atom(type: .open, value: "(")
|
||||||
|
#expect(atom.allowsScripts)
|
||||||
|
|
||||||
|
atom.`subscript` = Math.AtomList()
|
||||||
|
#expect(atom.`subscript` != nil)
|
||||||
|
|
||||||
|
atom.superscript = Math.AtomList()
|
||||||
|
#expect(atom.superscript != nil)
|
||||||
|
|
||||||
|
atom = Math.Atom(type: .boundary, value: "(")
|
||||||
|
#expect(!atom.allowsScripts)
|
||||||
|
#expect(atom.`subscript` == nil)
|
||||||
|
#expect(atom.superscript == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesAtomsWithScripts() throws {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let list2 = Math.AtomList()
|
||||||
|
list2.append(atom3)
|
||||||
|
list2.append(atom2)
|
||||||
|
|
||||||
|
let atom = Math.Atom(type: .open, value: "(")
|
||||||
|
atom.`subscript` = list
|
||||||
|
atom.superscript = list2
|
||||||
|
|
||||||
|
let copy = atom.copy()
|
||||||
|
try assertAtomCopyMatches(copy, original: atom, context: "atom copy")
|
||||||
|
try assertAtomListCopyMatches(
|
||||||
|
copy.superscript, original: atom.superscript, context: "superscript copy"
|
||||||
|
)
|
||||||
|
try assertAtomListCopyMatches(
|
||||||
|
copy.`subscript`, original: atom.`subscript`, context: "subscript copy"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesFraction() throws {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let list2 = Math.AtomList()
|
||||||
|
list2.append(atom3)
|
||||||
|
list2.append(atom2)
|
||||||
|
|
||||||
|
let fraction = Math.Fraction(hasRule: false)
|
||||||
|
#expect(fraction.type == .fraction)
|
||||||
|
fraction.numerator = list
|
||||||
|
fraction.denominator = list2
|
||||||
|
fraction.leftDelimiter = "a"
|
||||||
|
fraction.rightDelimiter = "b"
|
||||||
|
|
||||||
|
let copy = Math.Fraction(fraction)
|
||||||
|
try assertAtomCopyMatches(copy, original: fraction, context: "fraction copy")
|
||||||
|
try assertAtomListCopyMatches(
|
||||||
|
copy.numerator, original: fraction.numerator, context: "fraction numerator copy"
|
||||||
|
)
|
||||||
|
try assertAtomListCopyMatches(
|
||||||
|
copy.denominator, original: fraction.denominator, context: "fraction denominator copy"
|
||||||
|
)
|
||||||
|
#expect(!copy.hasRule)
|
||||||
|
#expect(copy.leftDelimiter == "a")
|
||||||
|
#expect(copy.rightDelimiter == "b")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesRadical() throws {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let list2 = Math.AtomList()
|
||||||
|
list2.append(atom3)
|
||||||
|
list2.append(atom2)
|
||||||
|
|
||||||
|
let radical = Math.Radical()
|
||||||
|
#expect(radical.type == .radical)
|
||||||
|
radical.radicand = list
|
||||||
|
radical.degree = list2
|
||||||
|
|
||||||
|
let copy = Math.Radical(radical)
|
||||||
|
try assertAtomCopyMatches(copy, original: radical, context: "radical copy")
|
||||||
|
try assertAtomListCopyMatches(copy.radicand, original: radical.radicand, context: "radicand copy")
|
||||||
|
try assertAtomListCopyMatches(copy.degree, original: radical.degree, context: "degree copy")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesLargeOperator() throws {
|
||||||
|
let largeOperator = Math.LargeOperator(limits: true)
|
||||||
|
largeOperator.nucleus = "lim"
|
||||||
|
#expect(largeOperator.type == .largeOperator)
|
||||||
|
#expect(largeOperator.limits)
|
||||||
|
|
||||||
|
let copy = Math.LargeOperator(largeOperator)
|
||||||
|
try assertAtomCopyMatches(copy, original: largeOperator, context: "large operator copy")
|
||||||
|
#expect(copy.limits == largeOperator.limits)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesInnerAtom() throws {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let inner = Math.Inner()
|
||||||
|
inner.innerList = list
|
||||||
|
inner.leftBoundary = Math.Atom(type: .boundary, value: "(")
|
||||||
|
inner.rightBoundary = Math.Atom(type: .boundary, value: ")")
|
||||||
|
#expect(inner.type == .inner)
|
||||||
|
|
||||||
|
let copy = Math.Inner(inner)
|
||||||
|
try assertAtomCopyMatches(copy, original: inner, context: "inner atom copy")
|
||||||
|
try assertAtomListCopyMatches(copy.innerList, original: inner.innerList, context: "inner list copy")
|
||||||
|
try assertAtomCopyMatches(copy.leftBoundary!, original: inner.leftBoundary, context: "left boundary copy")
|
||||||
|
try assertAtomCopyMatches(copy.rightBoundary!, original: inner.rightBoundary, context: "right boundary copy")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func setsInnerBoundaries() {
|
||||||
|
let inner = Math.Inner()
|
||||||
|
|
||||||
|
inner.leftBoundary = Math.Atom(type: .boundary, value: "(")
|
||||||
|
inner.rightBoundary = Math.Atom(type: .boundary, value: ")")
|
||||||
|
#expect(inner.leftBoundary != nil)
|
||||||
|
#expect(inner.rightBoundary != nil)
|
||||||
|
|
||||||
|
inner.leftBoundary = nil
|
||||||
|
inner.rightBoundary = nil
|
||||||
|
#expect(inner.leftBoundary == nil)
|
||||||
|
#expect(inner.rightBoundary == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesOverline() throws {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let overline = Math.Overline()
|
||||||
|
#expect(overline.type == .overline)
|
||||||
|
overline.innerList = list
|
||||||
|
|
||||||
|
let copy = Math.Overline(overline)
|
||||||
|
try assertAtomCopyMatches(copy, original: overline, context: "overline copy")
|
||||||
|
try assertAtomListCopyMatches(copy.innerList, original: overline.innerList, context: "overline list copy")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesUnderline() throws {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let underline = Math.Underline()
|
||||||
|
#expect(underline.type == .underline)
|
||||||
|
underline.innerList = list
|
||||||
|
|
||||||
|
let copy = Math.Underline(underline)
|
||||||
|
try assertAtomCopyMatches(copy, original: underline, context: "underline copy")
|
||||||
|
try assertAtomListCopyMatches(copy.innerList, original: underline.innerList, context: "underline list copy")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesAccent() throws {
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let accent = Math.Accent(value: "^")
|
||||||
|
#expect(accent.type == .accent)
|
||||||
|
accent.innerList = list
|
||||||
|
|
||||||
|
let copy = Math.Accent(accent)
|
||||||
|
try assertAtomCopyMatches(copy, original: accent, context: "accent copy")
|
||||||
|
try assertAtomListCopyMatches(copy.innerList, original: accent.innerList, context: "accent list copy")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesSpace() throws {
|
||||||
|
let space = Math.Space(amount: 3)
|
||||||
|
#expect(space.type == .space)
|
||||||
|
|
||||||
|
let copy = Math.Space(space)
|
||||||
|
try assertAtomCopyMatches(copy, original: space, context: "space copy")
|
||||||
|
#expect(space.amount == copy.amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesStyle() throws {
|
||||||
|
let style = Math.Style(level: .script)
|
||||||
|
#expect(style.type == .style)
|
||||||
|
|
||||||
|
let copy = Math.Style(style)
|
||||||
|
try assertAtomCopyMatches(copy, original: style, context: "style copy")
|
||||||
|
#expect(style.level == copy.level)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func createsTableAtom() {
|
||||||
|
let table = Math.Table()
|
||||||
|
#expect(table.type == .table)
|
||||||
|
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let list2 = Math.AtomList()
|
||||||
|
list2.append(atom3)
|
||||||
|
list2.append(atom2)
|
||||||
|
|
||||||
|
table.setCell(list, forRow: 3, column: 2)
|
||||||
|
table.setCell(list2, forRow: 1, column: 0)
|
||||||
|
|
||||||
|
table.setAlignment(.left, forColumn: 2)
|
||||||
|
table.setAlignment(.right, forColumn: 1)
|
||||||
|
|
||||||
|
#expect(table.cells.count == 4)
|
||||||
|
#expect(table.cells[0].count == 0)
|
||||||
|
#expect(table.cells[1].count == 1)
|
||||||
|
#expect(table.cells[2].count == 0)
|
||||||
|
#expect(table.cells[3].count == 3)
|
||||||
|
|
||||||
|
#expect(table.cells[1][0].atoms.count == 2)
|
||||||
|
#expect(table.cells[1][0] === list2)
|
||||||
|
|
||||||
|
#expect(table.cells[3][0].atoms.isEmpty)
|
||||||
|
#expect(table.cells[3][1].atoms.isEmpty)
|
||||||
|
#expect(table.cells[3][2] === list)
|
||||||
|
|
||||||
|
#expect(table.numberOfRows == 4)
|
||||||
|
#expect(table.numberOfColumns == 3)
|
||||||
|
|
||||||
|
#expect(table.alignments.count == 3)
|
||||||
|
#expect(table.alignments[0] == .center)
|
||||||
|
#expect(table.alignments[1] == .right)
|
||||||
|
#expect(table.alignments[2] == .left)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func copiesTableAtom() throws {
|
||||||
|
let table = Math.Table()
|
||||||
|
#expect(table.type == .table)
|
||||||
|
|
||||||
|
let list = Math.AtomList()
|
||||||
|
let atom1 = Math.AtomFactory.placeholder()
|
||||||
|
let atom2 = Math.AtomFactory.times()
|
||||||
|
let atom3 = Math.AtomFactory.divide()
|
||||||
|
list.append(atom1)
|
||||||
|
list.append(atom2)
|
||||||
|
list.append(atom3)
|
||||||
|
|
||||||
|
let list2 = Math.AtomList()
|
||||||
|
list2.append(atom3)
|
||||||
|
list2.append(atom2)
|
||||||
|
|
||||||
|
table.setCell(list, forRow: 0, column: 1)
|
||||||
|
table.setCell(list2, forRow: 0, column: 2)
|
||||||
|
|
||||||
|
table.setAlignment(.left, forColumn: 2)
|
||||||
|
table.setAlignment(.right, forColumn: 1)
|
||||||
|
table.interRowAdditionalSpacing = 3
|
||||||
|
table.interColumnSpacing = 10
|
||||||
|
|
||||||
|
let copy = Math.Table(table)
|
||||||
|
try assertAtomCopyMatches(copy, original: table, context: "table copy")
|
||||||
|
#expect(copy.interColumnSpacing == table.interColumnSpacing)
|
||||||
|
#expect(copy.interRowAdditionalSpacing == table.interRowAdditionalSpacing)
|
||||||
|
#expect(copy.alignments == table.alignments)
|
||||||
|
|
||||||
|
#expect(copy.cells[0].count == table.cells[0].count)
|
||||||
|
#expect(copy.cells[0][0].atoms.isEmpty)
|
||||||
|
#expect(copy.cells[0][0] !== table.cells[0][0])
|
||||||
|
try assertAtomListCopyMatches(copy.cells[0][1], original: list, context: "table list copy")
|
||||||
|
try assertAtomListCopyMatches(copy.cells[0][2], original: list2, context: "table list copy")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func parseFinalizedAtomList(from input: String) throws -> Math.AtomList {
|
||||||
|
let list = try #require(Math.Parser.build(fromString: input))
|
||||||
|
return list.finalized
|
||||||
|
}
|
||||||
|
|
||||||
|
private func assertFinalizedAtomListContents(_ finalized: Math.AtomList) throws {
|
||||||
|
#expect(finalized.atoms.count == 10, "Num atoms")
|
||||||
|
|
||||||
|
var atom = finalized.atoms[0]
|
||||||
|
try assertAtom(atom, type: .unaryOperator, nucleus: "−", range: NSRange(location: 0, length: 1))
|
||||||
|
|
||||||
|
atom = finalized.atoms[1]
|
||||||
|
try assertAtom(atom, type: .number, nucleus: "52", range: NSRange(location: 1, length: 2))
|
||||||
|
|
||||||
|
atom = finalized.atoms[2]
|
||||||
|
try assertAtom(atom, type: .variable, nucleus: "x", range: NSRange(location: 3, length: 1))
|
||||||
|
|
||||||
|
let superscript = try #require(atom.superscript)
|
||||||
|
#expect(superscript.atoms.count == 3, "Super script")
|
||||||
|
|
||||||
|
atom = superscript.atoms[0]
|
||||||
|
try assertAtom(atom, type: .number, nucleus: "13", range: NSRange(location: 0, length: 2))
|
||||||
|
|
||||||
|
atom = superscript.atoms[1]
|
||||||
|
try assertAtom(atom, type: .binaryOperator, nucleus: "+", range: NSRange(location: 2, length: 1))
|
||||||
|
|
||||||
|
atom = superscript.atoms[2]
|
||||||
|
try assertAtom(atom, type: .variable, nucleus: "y", range: NSRange(location: 3, length: 1))
|
||||||
|
|
||||||
|
atom = finalized.atoms[2]
|
||||||
|
let subscriptList = try #require(atom.`subscript`)
|
||||||
|
#expect(subscriptList.atoms.count == 2, "Sub script")
|
||||||
|
|
||||||
|
atom = subscriptList.atoms[0]
|
||||||
|
try assertAtom(atom, type: .number, nucleus: "15", range: NSRange(location: 0, length: 2))
|
||||||
|
|
||||||
|
atom = subscriptList.atoms[1]
|
||||||
|
try assertAtom(atom, type: .unaryOperator, nucleus: "−", range: NSRange(location: 2, length: 1))
|
||||||
|
|
||||||
|
atom = finalized.atoms[3]
|
||||||
|
try assertAtom(atom, type: .binaryOperator, nucleus: "+", range: NSRange(location: 4, length: 1))
|
||||||
|
|
||||||
|
atom = finalized.atoms[4]
|
||||||
|
try assertAtom(atom, type: .open, nucleus: "(", range: NSRange(location: 5, length: 1))
|
||||||
|
|
||||||
|
atom = finalized.atoms[5]
|
||||||
|
try assertAtom(atom, type: .unaryOperator, nucleus: "−", range: NSRange(location: 6, length: 1))
|
||||||
|
|
||||||
|
atom = finalized.atoms[6]
|
||||||
|
try assertAtom(atom, type: .number, nucleus: "12.3", range: NSRange(location: 7, length: 4))
|
||||||
|
|
||||||
|
atom = finalized.atoms[7]
|
||||||
|
try assertAtom(atom, type: .unaryOperator, nucleus: "*", range: NSRange(location: 11, length: 1))
|
||||||
|
|
||||||
|
atom = finalized.atoms[8]
|
||||||
|
try assertAtom(atom, type: .close, nucleus: ")", range: NSRange(location: 12, length: 1))
|
||||||
|
|
||||||
|
let fraction = try #require(finalized.atoms[9] as? Math.Fraction)
|
||||||
|
#expect(fraction.type == .fraction)
|
||||||
|
#expect(fraction.nucleus.isEmpty)
|
||||||
|
#expect(fraction.indexRange == NSRange(location: 13, length: 1))
|
||||||
|
|
||||||
|
let numerator = try #require(fraction.numerator)
|
||||||
|
#expect(numerator.atoms.count == 2, "Numerator")
|
||||||
|
|
||||||
|
atom = numerator.atoms[0]
|
||||||
|
try assertAtom(atom, type: .unaryOperator, nucleus: "−", range: NSRange(location: 0, length: 1))
|
||||||
|
|
||||||
|
atom = numerator.atoms[1]
|
||||||
|
try assertAtom(atom, type: .number, nucleus: "12", range: NSRange(location: 1, length: 2))
|
||||||
|
|
||||||
|
let denominator = try #require(fraction.denominator)
|
||||||
|
#expect(denominator.atoms.count == 1, "Denominator")
|
||||||
|
|
||||||
|
atom = denominator.atoms[0]
|
||||||
|
try assertAtom(atom, type: .number, nucleus: "15.2", range: NSRange(location: 0, length: 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func assertAtom(
|
||||||
|
_ atom: Math.Atom,
|
||||||
|
type: Math.AtomType,
|
||||||
|
nucleus: String,
|
||||||
|
range: NSRange
|
||||||
|
) throws {
|
||||||
|
#expect(atom.type == type)
|
||||||
|
#expect(atom.nucleus == nucleus)
|
||||||
|
#expect(atom.indexRange == range)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func assertAtomCopyMatches(
|
||||||
|
_ copy: Math.Atom?,
|
||||||
|
original: Math.Atom?,
|
||||||
|
context: String
|
||||||
|
) throws {
|
||||||
|
let copy = try #require(copy, "Missing copy for \(context)")
|
||||||
|
let original = try #require(original, "Missing original for \(context)")
|
||||||
|
#expect(copy.type == original.type, "\(context) type")
|
||||||
|
#expect(copy.nucleus == original.nucleus, "\(context) nucleus")
|
||||||
|
#expect(copy !== original, "\(context) identity")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func assertAtomListCopyMatches(
|
||||||
|
_ copy: Math.AtomList?,
|
||||||
|
original: Math.AtomList?,
|
||||||
|
context: String
|
||||||
|
) throws {
|
||||||
|
let copy = try #require(copy, "Missing copy for \(context)")
|
||||||
|
let original = try #require(original, "Missing original for \(context)")
|
||||||
|
#expect(copy.atoms.count == original.atoms.count, "\(context) count")
|
||||||
|
|
||||||
|
for (index, copyAtom) in copy.atoms.enumerated() {
|
||||||
|
let originalAtom = original.atoms[index]
|
||||||
|
try assertAtomCopyMatches(copyAtom, original: originalAtom, context: "\(context) atom \(index)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Math.AtomList {
|
||||||
|
func removeLastAtomForTesting() {
|
||||||
|
guard !atoms.isEmpty else { return }
|
||||||
|
atoms.removeLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeAtomForTesting(at index: Int) -> Bool {
|
||||||
|
guard atoms.indices.contains(index) else { return false }
|
||||||
|
atoms.remove(at: index)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeAtomsForTesting(in range: ClosedRange<Int>) -> Bool {
|
||||||
|
guard !atoms.isEmpty else { return false }
|
||||||
|
guard range.lowerBound >= 0, range.upperBound < atoms.count else { return false }
|
||||||
|
atoms.removeSubrange(range)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
//
|
|
||||||
// Untitled.swift
|
|
||||||
// swiftui-math
|
|
||||||
//
|
|
||||||
// Created by Guille Gonzalez on 31/12/25.
|
|
||||||
//
|
|
||||||
Reference in New Issue
Block a user