// Copyright 2017-present the Material Components for iOS authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #import #import "MDCMath.h" @interface MDCMathTests : XCTestCase @end @implementation MDCMathTests #pragma mark - MDCCeilScaled and MDCFloorScaled - (void)testMDCFloorScaled { CGFloat inputNumber = (CGFloat)1.3; CGFloat scale = 4; CGFloat expectedOutputNumber = (CGFloat)1.25; XCTAssertEqualWithAccuracy(MDCFloorScaled(inputNumber, scale), expectedOutputNumber, (CGFloat)0.001); } - (void)testMDCFloorScaledWhenScaleIsZero { CGFloat inputNumber = (CGFloat)1.3; CGFloat scale = 0; CGFloat expectedOutputNumber = 0; XCTAssertEqualWithAccuracy(MDCFloorScaled(inputNumber, scale), expectedOutputNumber, (CGFloat)0.001); } - (void)testMDCFloorScaledWhenScaleIsNegative { CGFloat inputNumber = (CGFloat)1.3; CGFloat scale = -2; CGFloat expectedOutputNumber = (CGFloat)1.5; XCTAssertEqualWithAccuracy(MDCFloorScaled(inputNumber, scale), expectedOutputNumber, (CGFloat)0.001); } - (void)testMDCCeilScaled { CGFloat inputNumber = (CGFloat)1.3; CGFloat scale = 4; CGFloat expectedOutputNumber = (CGFloat)1.5; XCTAssertEqualWithAccuracy(MDCCeilScaled(inputNumber, scale), expectedOutputNumber, (CGFloat)0.001); } - (void)testMDCCeilScaledWhenScaleIsZero { CGFloat inputNumber = (CGFloat)1.3; CGFloat scale = 0; CGFloat expectedOutputNumber = 0; XCTAssertEqualWithAccuracy(MDCCeilScaled(inputNumber, scale), expectedOutputNumber, (CGFloat)0.001); } - (void)testMDCCeilScaledWhenScaleIsNegative { CGFloat inputNumber = (CGFloat)1.3; CGFloat scale = -2; CGFloat expectedOutputNumber = 1; XCTAssertEqualWithAccuracy(MDCCeilScaled(inputNumber, scale), expectedOutputNumber, (CGFloat)0.001); } #pragma mark - MDCRect /** Basic test for alignment. */ - (void)testMDCRectAlignScale { // Given CGRect misalignedRect = CGRectMake((CGFloat)0.45, (CGFloat)0.78, (CGFloat)1.01, (CGFloat)5.98); CGRect alignedScale1Rect = CGRectMake(0, 0, 2, 7); CGRect alignedScale2Rect = CGRectMake(0, (CGFloat)0.5, (CGFloat)1.5, (CGFloat)6.5); CGRect alignedScale3Rect = CGRectMake((CGFloat)(1.0 / 3.0), (CGFloat)(2.0 / 3.0), (CGFloat)(4.0 / 3.0), (CGFloat)(19.0 / 3.0)); // Then CGRect outputScale1Rect = MDCRectAlignToScale(misalignedRect, 1); XCTAssertTrue(CGRectEqualToRect(alignedScale1Rect, outputScale1Rect)); XCTAssertTrue(CGRectContainsRect(outputScale1Rect, misalignedRect)); CGRect outputScale2Rect = MDCRectAlignToScale(misalignedRect, 2); XCTAssertTrue(CGRectEqualToRect(alignedScale2Rect, outputScale2Rect)); XCTAssertTrue(CGRectContainsRect(outputScale2Rect, misalignedRect)); CGRect outputScale3Rect = MDCRectAlignToScale(misalignedRect, 3); XCTAssertTrue(CGRectEqualToRect(alignedScale3Rect, outputScale3Rect)); XCTAssertTrue(CGRectContainsRect(outputScale3Rect, misalignedRect)); } /** Basic test of rectangle alignment when the origin has negative values. */ - (void)testMDCRectAlignScaleNegativeRectangle { // Given CGRect misalignedRect = CGRectMake((CGFloat)-5.01, (CGFloat)-0.399, (CGFloat)8.35, (CGFloat)2.65); CGRect alignedScale1Rect = CGRectMake(-6, -1, 10, 4); CGRect alignedScale2Rect = CGRectMake((CGFloat)-5.5, (CGFloat)-0.5, 9, 3); CGRect alignedScale3Rect = CGRectMake((CGFloat)(-16.0 / 3.0), (CGFloat)(-2.0 / 3.0), 9, 3); // Then CGRect outputScale1Rect = MDCRectAlignToScale(misalignedRect, 1); XCTAssertTrue(CGRectEqualToRect(alignedScale1Rect, outputScale1Rect)); XCTAssertTrue(CGRectContainsRect(outputScale1Rect, misalignedRect)); CGRect outputScale2Rect = MDCRectAlignToScale(misalignedRect, 2); XCTAssertTrue(CGRectEqualToRect(alignedScale2Rect, outputScale2Rect)); XCTAssertTrue(CGRectContainsRect(outputScale2Rect, misalignedRect)); CGRect outputScale3Rect = MDCRectAlignToScale(misalignedRect, 3); XCTAssertTrue(CGRectEqualToRect(alignedScale3Rect, outputScale3Rect)); XCTAssertTrue(CGRectContainsRect(outputScale3Rect, misalignedRect)); } /** Test of a non-standardized rectangle (height/width are negative) */ - (void)testMDCRectAlignScaleNonStandardRectangle { // Given CGRect misalignedRect = CGRectMake((CGFloat)17.9, (CGFloat)-4.44, (CGFloat)-10.10, (CGFloat)-15.85); // Standardized: (7.80, -20.29), (10.10, 15.85) CGRect alignedScale1Rect = CGRectMake(7, -21, 11, 17); CGRect alignedScale2Rect = CGRectMake((CGFloat)7.5, (CGFloat)-20.5, (CGFloat)10.5, (CGFloat)16.5); CGRect alignedScale3Rect = CGRectMake((CGFloat)(23.0 / 3.0), (CGFloat)(-61.0 / 3.0), (CGFloat)(31.0 / 3.0), 16); // Then CGRect outputScale1Rect = MDCRectAlignToScale(misalignedRect, 1); XCTAssertTrue(CGRectEqualToRect(alignedScale1Rect, outputScale1Rect)); XCTAssertTrue(CGRectContainsRect(outputScale1Rect, misalignedRect)); CGRect outputScale2Rect = MDCRectAlignToScale(misalignedRect, 2); XCTAssertTrue(CGRectEqualToRect(alignedScale2Rect, outputScale2Rect)); XCTAssertTrue(CGRectContainsRect(outputScale2Rect, misalignedRect)); CGRect outputScale3Rect = MDCRectAlignToScale(misalignedRect, 3); XCTAssertTrue(CGRectEqualToRect(alignedScale3Rect, outputScale3Rect)); XCTAssertTrue(CGRectContainsRect(outputScale3Rect, misalignedRect)); } /** Test that an already-aligned rectangle will not be changed. */ - (void)testMDCRectAlignScaleAlreadyAligned { // Given CGRect alignedScale1Rect = CGRectMake(10, 15, 5, 10); CGRect alignedScale2Rect = CGRectMake((CGFloat)10.5, (CGFloat)15.5, (CGFloat)5.5, (CGFloat)10.5); CGRect alignedScale3Rect = CGRectMake((CGFloat)(31.0 / 3.0), (CGFloat)(47.0 / 3.0), (CGFloat)(16.0 / 3.0), (CGFloat)(32.0 / 3.0)); // Then CGRect outputScale1Rect = MDCRectAlignToScale(alignedScale1Rect, 1); XCTAssertTrue(CGRectEqualToRect(alignedScale1Rect, outputScale1Rect)); XCTAssertTrue(CGRectContainsRect(outputScale1Rect, alignedScale1Rect)); CGRect outputScale2Rect = MDCRectAlignToScale(alignedScale2Rect, 2); XCTAssertTrue(CGRectEqualToRect(alignedScale2Rect, outputScale2Rect)); XCTAssertTrue(CGRectContainsRect(outputScale2Rect, alignedScale2Rect)); CGRect outputScale3Rect = MDCRectAlignToScale(alignedScale3Rect, 3); XCTAssertTrue(CGRectEqualToRect(alignedScale3Rect, outputScale3Rect)); XCTAssertTrue(CGRectContainsRect(outputScale3Rect, alignedScale3Rect)); } /** Test that a null rectangle returns CGRectNull. */ - (void)testMDCRectAlignScaleNullRectangle { // Then XCTAssertTrue(CGRectIsNull(MDCRectAlignToScale(CGRectNull, 1))); XCTAssertTrue(CGRectIsNull(MDCRectAlignToScale(CGRectNull, 2))); XCTAssertTrue(CGRectIsNull(MDCRectAlignToScale(CGRectNull, 3))); } /** Test that a scale value of zero returns the same as a scale of 1. */ - (void)testMDCRectAlignScaleZeroScale { // Given CGRect rectangle = CGRectMake((CGFloat)1.1, (CGFloat)2.2, (CGFloat)3.3, (CGFloat)4.4); // Then XCTAssertTrue( CGRectEqualToRect(MDCRectAlignToScale(rectangle, 0), MDCRectAlignToScale(rectangle, 1))); } #pragma mark - MDCPoint - (void)testMDCPointRoundWithScale { // Given CGPoint misalignedPoint = CGPointMake((CGFloat)0.7, (CGFloat)-1.3); CGPoint alignedScale1Point = CGPointMake(1, -1); CGPoint alignedScale2Point = CGPointMake((CGFloat)0.5, (CGFloat)-1.5); CGPoint alignedScale3Point = CGPointMake((CGFloat)(2.0 / 3.0), (CGFloat)(-4.0 / 3.0)); // Then XCTAssertTrue( CGPointEqualToPoint(alignedScale1Point, MDCPointRoundWithScale(misalignedPoint, 1))); XCTAssertTrue( CGPointEqualToPoint(alignedScale2Point, MDCPointRoundWithScale(misalignedPoint, 2))); XCTAssertTrue( CGPointEqualToPoint(alignedScale3Point, MDCPointRoundWithScale(misalignedPoint, 3))); } - (void)testMDCPointRoundScaleZeroScale { // Then XCTAssertTrue( CGPointEqualToPoint(CGPointZero, MDCPointRoundWithScale(CGPointMake((CGFloat)5.5, 13), 0))); } #pragma mark - MDCCenter - (void)testMDCRoundCenterWithBoundsAndScale { // Given CGPoint misalignedCenter = CGPointMake((CGFloat)0.7, (CGFloat)-1.3); CGRect bounds = CGRectMake(0, 0, 20, 21); CGPoint alignedScale1Center = CGPointMake(1, (CGFloat)-1.5); CGPoint alignedScale2Center = CGPointMake((CGFloat)0.5, (CGFloat)-1.5); CGPoint alignedScale3Center = CGPointMake((CGFloat)(2.0 / 3.0), (CGFloat)(-7.0 / 6.0)); // Then CGPoint outputScale1Center = MDCRoundCenterWithBoundsAndScale(misalignedCenter, bounds, 1); XCTAssertTrue(MDCCGFloatEqual(alignedScale1Center.x, outputScale1Center.x)); XCTAssertTrue(MDCCGFloatEqual(alignedScale1Center.y, outputScale1Center.y)); CGPoint outputScale2Center = MDCRoundCenterWithBoundsAndScale(misalignedCenter, bounds, 2); XCTAssertTrue(MDCCGFloatEqual(alignedScale2Center.x, outputScale2Center.x)); XCTAssertTrue(MDCCGFloatEqual(alignedScale2Center.y, outputScale2Center.y)); CGPoint outputScale3Center = MDCRoundCenterWithBoundsAndScale(misalignedCenter, bounds, 3); XCTAssertTrue(MDCCGFloatEqual(alignedScale3Center.x, outputScale3Center.x)); XCTAssertTrue(MDCCGFloatEqual(alignedScale3Center.y, outputScale3Center.y)); } - (void)testMDCRoundCenterWithBoundsAndScaleRoundingErrors { // Given #if CGFLOAT_IS_DOUBLE const CGFloat acceptableRoundingError = 5E-15; #else const CGFloat acceptableRoundingError = 5E-7f; #endif CGPoint misalignedCenter = CGPointMake((CGFloat)0.3, (CGFloat)9.99); CGRect bounds = CGRectMake(0, 0, (CGFloat)20.1, (CGFloat)21.9); CGPoint alignedScale1Center = CGPointMake((CGFloat)0.05, (CGFloat)9.95); CGPoint alignedScale2Center = CGPointMake((CGFloat)0.05, (CGFloat)9.95); CGPoint alignedScale3Center = CGPointMake((CGFloat)(0.05 + 1.0 / 3.0), (CGFloat)9.95); // Then CGPoint outputScale1Center = MDCRoundCenterWithBoundsAndScale(misalignedCenter, bounds, 1); XCTAssertLessThan(fabs(alignedScale1Center.x - outputScale1Center.x), acceptableRoundingError); XCTAssertLessThan(fabs(alignedScale1Center.y - outputScale1Center.y), acceptableRoundingError); CGPoint outputScale2Center = MDCRoundCenterWithBoundsAndScale(misalignedCenter, bounds, 2); XCTAssertLessThan(fabs(alignedScale2Center.x - outputScale2Center.x), acceptableRoundingError); XCTAssertLessThan(fabs(alignedScale2Center.y - outputScale2Center.y), acceptableRoundingError); CGPoint outputScale3Center = MDCRoundCenterWithBoundsAndScale(misalignedCenter, bounds, 3); XCTAssertLessThan(fabs(alignedScale3Center.x - outputScale3Center.x), acceptableRoundingError); XCTAssertLessThan(fabs(alignedScale3Center.y - outputScale3Center.y), acceptableRoundingError); } - (void)testMDCRoundCenterWithBoundsAndScaleZero { // Then XCTAssertTrue(CGPointEqualToPoint( CGPointZero, MDCRoundCenterWithBoundsAndScale(CGPointMake(-5, 10), CGRectMake(0, 0, 20, 20), 0))); } - (void)testMDCRoundCenterWithBoundsAndScaleNullBounds { // Then XCTAssertTrue(CGPointEqualToPoint( CGPointZero, MDCRoundCenterWithBoundsAndScale(CGPointMake(1, 2), CGRectNull, 1))); } - (void)testUIEdgeInsetsEqualToEdgeInsets { // Given CGFloat epsilon = 0; #if CGFLOAT_IS_DOUBLE epsilon = DBL_EPSILON; #else epsilon = FLT_EPSILON; #endif // When UIEdgeInsets insets1 = UIEdgeInsetsMake(1, 1, 1, 1); UIEdgeInsets insets2 = UIEdgeInsetsMake(1 + epsilon, 1 + epsilon, 1 + epsilon, 1 + epsilon); // Then XCTAssertFalse(UIEdgeInsetsEqualToEdgeInsets(insets1, insets2)); } - (void)testMDCEdgeInsetsEqualToEdgeInsets { // Given CGFloat epsilon = 0; #if CGFLOAT_IS_DOUBLE epsilon = DBL_EPSILON; #else epsilon = FLT_EPSILON; #endif // When UIEdgeInsets insets1 = UIEdgeInsetsMake(1, 1, 1, 1); UIEdgeInsets insets2 = UIEdgeInsetsMake(1 + epsilon, 1 + epsilon, 1 + epsilon, 1 + epsilon); // Then XCTAssertTrue(MDCEdgeInsetsEqualToEdgeInsets(insets1, insets2)); } @end