Przeglądaj źródła

Add ParalellWorker tests

Fix tasks being executed right away and worker doen't waited for completion

Signed-off-by: Henrik Storch <henrik.storch@nextcloud.com>
Henrik Storch 3 lat temu
rodzic
commit
5512da1e7e

+ 10 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -28,6 +28,7 @@
 		AF2D7C7C2742556F00ADF566 /* NCShareLinkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2D7C7B2742556F00ADF566 /* NCShareLinkCell.swift */; };
 		AF2D7C7E2742559100ADF566 /* NCShareUserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */; };
 		AF36077127BFA4E8001A243D /* ParallelWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF36077027BFA4E8001A243D /* ParallelWorker.swift */; };
+		AF36077627BFB019001A243D /* ParallelWorkerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF36077527BFB019001A243D /* ParallelWorkerTest.swift */; };
 		AF3FDCC22796ECC300710F60 /* NCTrash+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */; };
 		AF3FDCC32796F3FB00710F60 /* NCTrashListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell.swift */; };
 		AF4BF614275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; };
@@ -467,6 +468,7 @@
 		AF2D7C7B2742556F00ADF566 /* NCShareLinkCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareLinkCell.swift; sourceTree = "<group>"; };
 		AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareUserCell.swift; sourceTree = "<group>"; };
 		AF36077027BFA4E8001A243D /* ParallelWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallelWorker.swift; sourceTree = "<group>"; };
+		AF36077527BFB019001A243D /* ParallelWorkerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallelWorkerTest.swift; sourceTree = "<group>"; };
 		AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCTrash+CollectionView.swift"; sourceTree = "<group>"; };
 		AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Account.swift"; sourceTree = "<group>"; };
 		AF4BF61827562A4B0081CEEF /* NCManageDatabse+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabse+Metadata.swift"; sourceTree = "<group>"; };
@@ -963,6 +965,7 @@
 			isa = PBXGroup;
 			children = (
 				AF8ED2022757822700B8DBC4 /* NCGlobalTests.swift */,
+				AF36077527BFB019001A243D /* ParallelWorkerTest.swift */,
 				AF8ED1FB2757821000B8DBC4 /* NextcloudTests.swift */,
 			);
 			path = NextcloudTests;
@@ -2260,6 +2263,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				AF36077627BFB019001A243D /* ParallelWorkerTest.swift in Sources */,
 				AF8ED1FC2757821000B8DBC4 /* NextcloudTests.swift in Sources */,
 				AF8ED2032757822700B8DBC4 /* NCGlobalTests.swift in Sources */,
 			);
@@ -2616,6 +2620,9 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
+				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+				ENABLE_HARDENED_RUNTIME = YES;
+				GENERATE_INFOPLIST_FILE = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Nextcloud.app/Nextcloud";
@@ -2626,6 +2633,9 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
+				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+				ENABLE_HARDENED_RUNTIME = YES;
+				GENERATE_INFOPLIST_FILE = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Nextcloud.app/Nextcloud";

+ 85 - 0
NextcloudTests/ParallelWorkerTest.swift

@@ -0,0 +1,85 @@
+//
+//  ParallelWorkerTest.swift
+//  Nextcloud
+//
+//  Created by Henrik Storch on 18.02.22.
+//  Copyright © 2021 Henrik Storch. All rights reserved.
+//
+//  Author Henrik Storch <henrik.storch@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+@testable import Nextcloud
+import XCTest
+
+class ParallelWorkerTest: XCTestCase {
+
+    func testWorkerComplete() throws {
+        let expectation = XCTestExpectation(description: "Worker executes all tasks")
+        let taskCount = 20
+        var tasksComplete = 0
+        let worker = ParallelWorker(n: 5, titleKey: nil, totalTasks: nil, hudView: nil)
+        for _ in 0..<taskCount {
+            worker.execute { completion in
+                tasksComplete += 1
+                completion()
+            }
+        }
+        worker.completeWork {
+            XCTAssertEqual(tasksComplete, taskCount)
+            if tasksComplete == taskCount {
+                expectation.fulfill()
+            }
+        }
+
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(result, .completed)
+    }
+
+    func testWorkerOrder() throws {
+        let expectation = XCTestExpectation(description: "Worker executes work in sequence for n = 1")
+        let sortedArray = Array(0..<20)
+        var array: [Int] = []
+        let worker = ParallelWorker(n: 1, titleKey: nil, totalTasks: nil, hudView: nil)
+        for i in sortedArray {
+            worker.execute { completion in
+                DispatchQueue.main.asyncAfter(deadline: .now() + Double.random(in: 0...0.2)) {
+                    array.append(i)
+                    completion()
+                }
+            }
+        }
+        worker.completeWork {
+            XCTAssertEqual(sortedArray, array)
+            if sortedArray == array {
+                expectation.fulfill()
+            }
+        }
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(result, .completed)
+    }
+
+    func testWorkerFailsWithoutCompletion() throws {
+        let expectation = XCTestExpectation(description: "Worker fails if completion isn't called")
+        expectation.isInverted = true
+        let worker = ParallelWorker(n: 5, titleKey: nil, totalTasks: nil, hudView: nil)
+        for _ in 0..<20 {
+            worker.execute { _ in }
+        }
+        worker.completeWork { expectation.fulfill() }
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(result, .completed)
+    }
+}

+ 3 - 3
iOSClient/Utility/ParallelWorker.swift

@@ -65,10 +65,10 @@ class ParallelWorker {
     /// Execute
     /// - Parameter task: The task to execute. Needs to call `completion()` when done so the next task can be executed.
     func execute(task: @escaping (_ completion: @escaping () -> Void) -> Void) {
+        completionGroup.enter()
         queue.async {
             self.semaphore.wait()
-            guard !self.isCancelled else { return }
-            self.completionGroup.enter()
+            guard !self.isCancelled else { return self.completionGroup.leave() }
             task {
                 self.completedTasks += 1
                 DispatchQueue.main.async {
@@ -79,8 +79,8 @@ class ParallelWorker {
                         self.hud?.textLabel.text?.append(NSLocalizedString("_files_", comment: ""))
                     }
                 }
-                self.completionGroup.leave()
                 self.semaphore.signal()
+                self.completionGroup.leave()
             }
         }
     }