fixed division by zero in DivActionHandler.

05ec0af3e193df3b57525fb76d3a1a3a322b8477
This commit is contained in:
morevsavva
2024-02-14 16:23:26 +03:00
parent c4e98e1993
commit 2217f08db9
5 changed files with 195 additions and 5 deletions
+1
View File
@@ -9687,6 +9687,7 @@
"client/ios/DivKitSnapshotTests/YSFontProvider.swift":"divkit/public/client/ios/DivKitSnapshotTests/YSFontProvider.swift",
"client/ios/DivKitTests/Actions/DivActionHandlerTests.swift":"divkit/public/client/ios/DivKitTests/Actions/DivActionHandlerTests.swift",
"client/ios/DivKitTests/Actions/DivActionIntentTests.swift":"divkit/public/client/ios/DivKitTests/Actions/DivActionIntentTests.swift",
"client/ios/DivKitTests/Actions/DivActionURLHandlerTests.swift":"divkit/public/client/ios/DivKitTests/Actions/DivActionURLHandlerTests.swift",
"client/ios/DivKitTests/DivBlockModelingContextTests.swift":"divkit/public/client/ios/DivKitTests/DivBlockModelingContextTests.swift",
"client/ios/DivKitTests/DivBlockStateStorageTests.swift":"divkit/public/client/ios/DivKitTests/DivBlockStateStorageTests.swift",
"client/ios/DivKitTests/DivDeserializationErrorsTests.swift":"divkit/public/client/ios/DivKitTests/DivDeserializationErrorsTests.swift",
@@ -10,6 +10,7 @@
0A9A9AA028CB1A9200D1D810 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 0A9A9A9F28CB1A9200D1D810 /* Lottie */; };
0AB4193E28BCF61D00FDAAD5 /* LottieAnimationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB4193D28BCF61D00FDAAD5 /* LottieAnimationFactory.swift */; };
431276EE2A72B8F2000CC821 /* DivView+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431276ED2A72B8F2000CC821 /* DivView+SwiftUI.swift */; };
431CF8582B7BC47E005A2E61 /* DivActionURLHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CF8572B7BC47E005A2E61 /* DivActionURLHandlerTests.swift */; };
431DC9C22961EF84007FA268 /* ContainerBlockLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431DC9C12961EF84007FA268 /* ContainerBlockLayoutTests.swift */; };
431DC9C429620AA1007FA268 /* BlockTestsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431DC9C329620AA1007FA268 /* BlockTestsModels.swift */; };
435653DB29742C9300224B9D /* BlockWithFixedWrapContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435653DA29742C9300224B9D /* BlockWithFixedWrapContent.swift */; };
@@ -275,6 +276,7 @@
/* Begin PBXFileReference section */
0AB4193D28BCF61D00FDAAD5 /* LottieAnimationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieAnimationFactory.swift; sourceTree = "<group>"; };
431276ED2A72B8F2000CC821 /* DivView+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DivView+SwiftUI.swift"; sourceTree = "<group>"; };
431CF8572B7BC47E005A2E61 /* DivActionURLHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DivActionURLHandlerTests.swift; sourceTree = "<group>"; };
431DC9C12961EF84007FA268 /* ContainerBlockLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerBlockLayoutTests.swift; sourceTree = "<group>"; };
431DC9C329620AA1007FA268 /* BlockTestsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockTestsModels.swift; sourceTree = "<group>"; };
435653DA29742C9300224B9D /* BlockWithFixedWrapContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockWithFixedWrapContent.swift; sourceTree = "<group>"; };
@@ -744,6 +746,7 @@
children = (
8C7B1B312865C01C0036EF4C /* DivActionIntentTests.swift */,
8C40BA012ABB3B5C008405AD /* DivActionHandlerTests.swift */,
431CF8572B7BC47E005A2E61 /* DivActionURLHandlerTests.swift */,
);
path = Actions;
sourceTree = "<group>";
@@ -1638,6 +1641,7 @@
8C7B1CA92865C01E0036EF4C /* DivStateManagerTests.swift in Sources */,
8C7B1BFD2865C01C0036EF4C /* DivSeparatorExtensionsTests.swift in Sources */,
8C23F41229DDB3810069F3F7 /* EntityTemplate.swift in Sources */,
431CF8582B7BC47E005A2E61 /* DivActionURLHandlerTests.swift in Sources */,
8C23F43629DDB3810069F3F7 /* EntityWithStringEnumPropertyWithDefaultValueTemplate.swift in Sources */,
8C23F41C29DDB3810069F3F7 /* EntityWithArrayWithTransformTemplate.swift in Sources */,
8CD1521028CF217B0061128B /* ExpressionResolverTests.swift in Sources */,
@@ -214,6 +214,9 @@ public final class DivActionURLHandler {
count: Int,
overflow: OverflowMode
) -> Int {
guard count != 0 else {
return current
}
switch overflow {
case .ring:
return (current + 1) % count
@@ -270,9 +273,12 @@ public final class DivActionURLHandler {
count: Int,
overflow: OverflowMode
) -> Int {
guard count != 0 else {
return current
}
switch overflow {
case .ring:
return (count + (current - 1)) % count
return (current + count - 1) % count
case .clamp:
return max(0, current - 1)
}
@@ -284,18 +290,22 @@ public final class DivActionURLHandler {
index: Int,
itemsCount: Int
) {
let clampedIndex = clamp(index, min: 0, max: max(0, itemsCount - 1))
guard clampedIndex == index else {
return
}
blockStateStorage.setState(
id: id,
cardId: cardId,
state: GalleryViewState(
contentPageIndex: CGFloat(max(0, index)),
contentPageIndex: CGFloat(clampedIndex),
itemsCount: itemsCount
)
)
}
private func setPagerCurrentItem(id: String, cardId: DivCardID, index: Int, numberOfPages: Int) {
let clampedIndex = clamp(index, min: 0, max: numberOfPages - 1)
let clampedIndex = clamp(index, min: 0, max: max(0, numberOfPages - 1))
guard clampedIndex == index else {
return
}
@@ -311,11 +321,15 @@ public final class DivActionURLHandler {
}
private func setTabsCurrentItem(id: String, cardId: DivCardID, index: Int, countOfPages: Int) {
let clampedIndex = clamp(index, min: 0, max: max(0, countOfPages - 1))
guard clampedIndex == index else {
return
}
blockStateStorage.setState(
id: id,
cardId: cardId,
state: TabViewState(
selectedPageIndex: CGFloat(max(0, index)),
selectedPageIndex: CGFloat(clampedIndex),
countOfPages: countOfPages
)
)
@@ -148,7 +148,7 @@ private final class MockActionLogger: DivActionLogger {
}
}
private final class MockPatchProvider: DivPatchProvider {
final class MockPatchProvider: DivPatchProvider {
func getPatch(url _: URL, completion _: @escaping DivPatchProviderCompletion) {}
func cancelRequests() {}
@@ -0,0 +1,171 @@
@testable import DivKit
@testable import LayoutKit
import XCTest
final class DivActionURLHandlerTests: XCTestCase {
private var actionHandler: DivActionURLHandler!
private let blockStateStorage = DivBlockStateStorage()
override func setUp() {
actionHandler = DivActionURLHandler(
stateUpdater: DefaultDivStateManagement(),
blockStateStorage: blockStateStorage,
patchProvider: MockPatchProvider(),
variableUpdater: DivVariablesStorage(),
updateCard: { _ in },
showTooltip: nil,
tooltipActionPerformer: nil,
persistentValuesStorage: DivPersistentValuesStorage()
)
}
func test_setNextItemAction() {
setItemTestCases(
beforeStates: SetItemAction.firstElementState,
afterState: SetItemAction.secondElementState,
mode: .next(.clamp)
)
}
func test_setNextItemActionWithRingOverflow() {
setItemTestCases(
beforeStates: SetItemAction.lastElementsStates,
afterState: SetItemAction.firstElementState,
mode: .next(.ring)
)
}
func test_setNextItemActionWithEmptyState() {
setItemTestCases(
beforeStates: SetItemAction.emptyStates,
afterState: SetItemAction.emptyStates,
mode: .next(.clamp)
)
}
func test_setPreviousItemAction() {
setItemTestCases(
beforeStates: SetItemAction.secondElementState,
afterState: SetItemAction.firstElementState,
mode: .previous(.clamp)
)
}
func test_setPreviousItemActionWithRingOverflow() {
setItemTestCases(
beforeStates: SetItemAction.firstElementState,
afterState: SetItemAction.lastElementsStates,
mode: .previous(.ring)
)
}
func test_setPreviousItemActionWithEmptyState() {
setItemTestCases(
beforeStates: SetItemAction.emptyStates,
afterState: SetItemAction.emptyStates,
mode: .previous(.clamp)
)
}
func test_setCurrentItemAction() {
setItemTestCases(
beforeStates: SetItemAction.firstElementState,
afterState: SetItemAction.firstElementState,
mode: .current(0)
)
setItemTestCases(
beforeStates: SetItemAction.firstElementState,
afterState: SetItemAction.secondElementState,
mode: .current(1)
)
}
func test_setCurrentItemActionWithOverflow() {
setItemTestCases(
beforeStates: SetItemAction.firstElementState,
afterState: SetItemAction.firstElementState,
mode: .current(50)
)
}
private func setItemTestCases(
beforeStates: [ElementState],
afterState: [ElementState],
mode: SetItemAction.Mode
) {
zip(beforeStates, afterState).forEach {
switch $0 {
case let (before as GalleryViewState, after as GalleryViewState):
setItemTestCase(beforeState: before, afterState: after, mode: mode)
case let (before as PagerViewState, after as PagerViewState):
setItemTestCase(beforeState: before, afterState: after, mode: mode)
case let (before as TabViewState, after as TabViewState):
setItemTestCase(beforeState: before, afterState: after, mode: mode)
case (_, _):
assertionFailure("Unsupported element state")
}
}
}
private func setItemTestCase<State: ElementState & Equatable>(
beforeState: State,
afterState: State,
mode: SetItemAction.Mode
) {
blockStateStorage.setState(id: elementID, cardId: cardID, state: beforeState)
let _ = actionHandler.handleURL(SetItemAction.makeURL(mode: mode), cardId: cardID)
XCTAssertEqual(blockStateStorage.getState(elementID, cardId: cardID), afterState)
}
}
private enum SetItemAction {
enum Mode {
enum Overflow: String {
case clamp
case ring
}
case next(Overflow)
case previous(Overflow)
case current(Int)
}
static let firstElementState: [ElementState] = [
PagerViewState(numberOfPages: 10, currentPage: 0),
GalleryViewState(contentPageIndex: 0, itemsCount: 10),
TabViewState(selectedPageIndex: 0, countOfPages: 10),
]
static let secondElementState: [ElementState] = [
PagerViewState(numberOfPages: 10, currentPage: 1),
GalleryViewState(contentPageIndex: 1, itemsCount: 10),
TabViewState(selectedPageIndex: 1, countOfPages: 10),
]
static let lastElementsStates: [ElementState] = [
PagerViewState(numberOfPages: 10, currentPage: 9),
GalleryViewState(contentPageIndex: 9, itemsCount: 10),
TabViewState(selectedPageIndex: 9, countOfPages: 10),
]
static let emptyStates: [ElementState] = [
PagerViewState(numberOfPages: 0, currentPage: 0),
GalleryViewState(contentPageIndex: 0, itemsCount: 0),
TabViewState(selectedPageIndex: 0, countOfPages: 0),
]
static func makeURL(mode: Mode) -> URL {
switch mode {
case let .next(overflow):
url("div-action://set_next_item?id=\(elementID)&overflow=\(overflow)")
case let .previous(overflow):
url("div-action://set_previous_item?id=\(elementID)&overflow=\(overflow)")
case let .current(item):
url("div-action://set_current_item?id=\(elementID)&item=\(item)")
}
}
}
private let cardID: DivCardID = "cardID"
private let elementID: String = "element"