| // 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 <XCTest/XCTest.h> |
| #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 |