Skip to content

Commit

Permalink
Even safer validation of slice size
Browse files Browse the repository at this point in the history
Negative sizes are not possible anymore

Signed-off-by: Henrik Panhans <[email protected]>
  • Loading branch information
henrik-dmg committed Mar 11, 2024
1 parent 8c2949e commit b6c1749
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 14 deletions.
12 changes: 10 additions & 2 deletions Sources/SwiftFrameCore/Config/DeviceData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ struct DeviceData: Decodable, ConfigValidateable {
)
}

if let templateImage {
_ = try SliceSizeCalculator.calculateSliceSize(
templateImageSize: templateImage.ky_nativeSize,
numberOfSlices: numberOfSlices,
gapWidth: gapWidth
)
}

try screenshotData.forEach { try $0.validate() }
try textData.forEach { try $0.validate() }

Expand All @@ -134,12 +142,12 @@ struct DeviceData: Decodable, ConfigValidateable {
CommandLineFormatter.printKeyValue("Gap Width", value: gapWidth, insetBy: tabs)

if let templateImage {
let sliceSize = SliceSizeCalculator.calculateSliceSize(
let sliceSize = try? SliceSizeCalculator.calculateSliceSize(
templateImageSize: templateImage.ky_nativeSize,
numberOfSlices: numberOfSlices,
gapWidth: gapWidth
)
CommandLineFormatter.printKeyValue("Output slice size", value: sliceSize.configValidationRepresentation, insetBy: tabs)
CommandLineFormatter.printKeyValue("Output slice size", value: sliceSize?.configValidationRepresentation, insetBy: tabs)
}

CommandLineFormatter.printKeyValue(
Expand Down
3 changes: 2 additions & 1 deletion Sources/SwiftFrameCore/Extensions/NSError+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public extension NSError {
NSLocalizedDescriptionKey: description,
NSError.kExpectationKey: expectation as Any,
NSError.kActualValueKey: actualValue as Any
])
]
)
}

var expectation: String? {
Expand Down
17 changes: 17 additions & 0 deletions Sources/SwiftFrameCore/Helper Types/Pluralizer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Foundation

enum Pluralizer {

static func pluralize(_ value: Int, singular: String, plural: String, zero: String? = nil) -> String {
let absoluteValue = abs(value)
return switch absoluteValue {
case 0:
"\(value) \(zero ?? plural)"
case 1:
"\(value) \(singular)"
default:
"\(value) \(plural)"
}
}

}
8 changes: 2 additions & 6 deletions Sources/SwiftFrameCore/Workers/ConfigProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,8 @@ public class ConfigProcessor: VerbosePrintable {
// MARK: - Methods

public func validate() throws {
try process()
try data.validate()
}

private func process() throws {
try data.process()
try data.validate()
}

public func run() throws {
Expand Down Expand Up @@ -109,7 +105,7 @@ public class ConfigProcessor: VerbosePrintable {
throw NSError(description: "No template image found")
}

let sliceSize = SliceSizeCalculator.calculateSliceSize(
let sliceSize = try SliceSizeCalculator.calculateSliceSize(
templateImageSize: templateImage.ky_nativeSize,
numberOfSlices: deviceData.numberOfSlices,
gapWidth: deviceData.gapWidth
Expand Down
15 changes: 14 additions & 1 deletion Sources/SwiftFrameCore/Workers/SliceSizeCalculator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@ import Foundation

struct SliceSizeCalculator {

static func calculateSliceSize(templateImageSize: CGSize, numberOfSlices: Int, gapWidth: Int?) -> CGSize {
static func calculateSliceSize(templateImageSize: CGSize, numberOfSlices: Int, gapWidth: Int?) throws -> CGSize {
guard numberOfSlices > 0 else {
throw NSError(description: "Number of slices must be larger than 0")
}
// number of slices minus 1 because gaps are only in between, multiplied by gapWidth
let totalGapWidthIfAny = gapWidth.flatMap { (numberOfSlices - 1) * $0 }
let templateWidthSubstractingGaps = templateImageSize.width - CGFloat(totalGapWidthIfAny ?? 0)

guard Int(templateWidthSubstractingGaps) >= numberOfSlices else {
let minimumTemplateWidth = numberOfSlices + (totalGapWidthIfAny ?? 0)
throw NSError(
description: "Template image is not wide enough to accommodate \(Pluralizer.pluralize(numberOfSlices, singular: "slice", plural: "slices"))",
expectation: "Template image should be at least \(minimumTemplateWidth) pixels wide",
actualValue: "Template image is \(templateImageSize.width) pixels wide"
)
}

// Resulting slice is remaining width divided by expected number of slices, height can just be forwarded
return CGSize(width: templateWidthSubstractingGaps / CGFloat(numberOfSlices), height: templateImageSize.height)
}
Expand Down
23 changes: 23 additions & 0 deletions Tests/SwiftFrameTests/PluralizerTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Foundation
import XCTest
@testable import SwiftFrameCore

class PluralizerTests: XCTestCase {

func testPluralizer_ProducesSingularString_WhenSpecifyingOne() {
XCTAssertEqual(Pluralizer.pluralize(1, singular: "slice", plural: "slices"), "1 slice")
}

func testPluralizer_ProducesPluralString_WhenSpecifyingBigNumber() {
XCTAssertEqual(Pluralizer.pluralize(32, singular: "slice", plural: "slices"), "32 slices")
}

func testPluralizer_ProducesPluralString_WhenSpecifyingZero() {
XCTAssertEqual(Pluralizer.pluralize(0, singular: "slice", plural: "slices"), "0 slices")
}

func testPluralizer_ProducesZeroString_WhenSpecifyingZero() {
XCTAssertEqual(Pluralizer.pluralize(0, singular: "slice", plural: "slices", zero: "bogus"), "0 bogus")
}

}
20 changes: 16 additions & 4 deletions Tests/SwiftFrameTests/SliceSizeCalculatorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,38 @@ import XCTest

final class SliceSizeCalculatorTests: BaseTestCase {

func testSliceSizeCalculator_ProducesFiveSlices_WhenNotUsingGapWidth() {
func testSliceSizeCalculator_ProducesFiveSlices_WhenNotUsingGapWidth() throws {
let templateSize = CGSize(width: 100, height: 50)

let calculatedSliceSize = SliceSizeCalculator.calculateSliceSize(
let calculatedSliceSize = try SliceSizeCalculator.calculateSliceSize(
templateImageSize: templateSize,
numberOfSlices: 5,
gapWidth: nil
)
XCTAssertEqual(calculatedSliceSize, CGSize(width: 20, height: 50))
}

func testSliceSizeCalculator_ProducesFiveSlices_WhenUsingGapWidth() {
func testSliceSizeCalculator_ProducesFiveSlices_WhenUsingGapWidth() throws {
let templateSize = CGSize(width: 100, height: 50)

let calculatedSliceSize = SliceSizeCalculator.calculateSliceSize(
let calculatedSliceSize = try SliceSizeCalculator.calculateSliceSize(
templateImageSize: templateSize,
numberOfSlices: 5,
gapWidth: 5
)
XCTAssertEqual(calculatedSliceSize, CGSize(width: 16, height: 50))
}

func testSliceSizeCalculator_ThrowsError_WhenTotalWidthIsNotEnough() {
let templateSize = CGSize(width: 24, height: 50)

XCTAssertThrowsError(
try SliceSizeCalculator.calculateSliceSize(
templateImageSize: templateSize,
numberOfSlices: 7,
gapWidth: 6
)
)
}

}

0 comments on commit b6c1749

Please sign in to comment.