Selaa lähdekoodia

优化 - 文件对比支持外部显示进度; 文件对比支持 MD5码做摘要精对照过滤;

zhudongyong 2 vuotta sitten
vanhempi
commit
207dcda59f

+ 8 - 0
KdanAutoTest/KdanAuto.xcodeproj/project.pbxproj

@@ -29,6 +29,7 @@
 		240934B42992735200839CC8 /* FileConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240934B32992735200839CC8 /* FileConverter.swift */; };
 		240934B72992737200839CC8 /* ImageProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240934B62992737200839CC8 /* ImageProcess.swift */; };
 		2411D9CD29ABAF36009DE4A8 /* PDFKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2411D9CC29ABAF36009DE4A8 /* PDFKit.framework */; };
+		2421BE4229C049A700E43F9F /* CryptoKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2421BE4129C049A700E43F9F /* CryptoKit.framework */; };
 		242F966D298BAE2200CFF56C /* Toggle_off.png in Resources */ = {isa = PBXBuildFile; fileRef = 242F966A298BAE2200CFF56C /* Toggle_off.png */; };
 		242F966E298BAE2200CFF56C /* Toggle_on.png in Resources */ = {isa = PBXBuildFile; fileRef = 242F966B298BAE2200CFF56C /* Toggle_on.png */; };
 		242F966F298BAE2200CFF56C /* Toggle_half.png in Resources */ = {isa = PBXBuildFile; fileRef = 242F966C298BAE2200CFF56C /* Toggle_half.png */; };
@@ -151,6 +152,8 @@
 		240934B32992735200839CC8 /* FileConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileConverter.swift; sourceTree = "<group>"; };
 		240934B62992737200839CC8 /* ImageProcess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProcess.swift; sourceTree = "<group>"; };
 		2411D9CC29ABAF36009DE4A8 /* PDFKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PDFKit.framework; path = System/Library/Frameworks/PDFKit.framework; sourceTree = SDKROOT; };
+		2421BE3F29C0489400E43F9F /* libcommonCrypto.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcommonCrypto.tbd; path = usr/lib/system/libcommonCrypto.tbd; sourceTree = SDKROOT; };
+		2421BE4129C049A700E43F9F /* CryptoKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CryptoKit.framework; path = System/Library/Frameworks/CryptoKit.framework; sourceTree = SDKROOT; };
 		242F966A298BAE2200CFF56C /* Toggle_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Toggle_off.png; sourceTree = "<group>"; };
 		242F966B298BAE2200CFF56C /* Toggle_on.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Toggle_on.png; sourceTree = "<group>"; };
 		242F966C298BAE2200CFF56C /* Toggle_half.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Toggle_half.png; sourceTree = "<group>"; };
@@ -228,6 +231,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				2421BE4229C049A700E43F9F /* CryptoKit.framework in Frameworks */,
 				24FE3FF329BAF9B8004F4DD4 /* CoreImage.framework in Frameworks */,
 				2400D7DE29B9FCC90059AC45 /* libopencv_world.dylib in Frameworks */,
 				2411D9CD29ABAF36009DE4A8 /* PDFKit.framework in Frameworks */,
@@ -299,6 +303,8 @@
 		240509AF29433CDE00B501B2 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				2421BE4129C049A700E43F9F /* CryptoKit.framework */,
+				2421BE3F29C0489400E43F9F /* libcommonCrypto.tbd */,
 				24FE3FF229BAF9B8004F4DD4 /* CoreImage.framework */,
 				2411D9CC29ABAF36009DE4A8 /* PDFKit.framework */,
 				240509B129433CDE00B501B2 /* QuickLook.framework */,
@@ -1077,6 +1083,7 @@
 					"$(PROJECT_DIR)/KdanAuto/Third\\ Part\\ Library/otherlib",
 					"$(PROJECT_DIR)/KdanAuto/Third\\ Part\\ Library/otherlib",
 					"$(PROJECT_DIR)/KdanAuto/Third\\ Part\\ Library/otherlib",
+					"$(SDKROOT)/usr/lib/system",
 				);
 				MACOSX_DEPLOYMENT_TARGET = 11.0;
 				MARKETING_VERSION = 1.0;
@@ -1127,6 +1134,7 @@
 					"$(PROJECT_DIR)/KdanAuto/Third\\ Part\\ Library/otherlib",
 					"$(PROJECT_DIR)/KdanAuto/Third\\ Part\\ Library/otherlib",
 					"$(PROJECT_DIR)/KdanAuto/Third\\ Part\\ Library/otherlib",
+					"$(SDKROOT)/usr/lib/system",
 				);
 				MACOSX_DEPLOYMENT_TARGET = 11.0;
 				MARKETING_VERSION = 1.0;

BIN
KdanAutoTest/KdanAuto.xcodeproj/project.xcworkspace/xcuserdata/zhudongyong.xcuserdatad/UserInterfaceState.xcuserstate


+ 2 - 2
KdanAutoTest/KdanAuto.xcodeproj/xcuserdata/zhudongyong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -110,8 +110,8 @@
             filePath = "ProcessCheckFile/Process/ProcessThumbnal.swift"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "105"
-            endingLineNumber = "105"
+            startingLineNumber = "111"
+            endingLineNumber = "111"
             landmarkName = "process(_:desPath:outputSize:)"
             landmarkType = "7">
          </BreakpointContent>

+ 1 - 1
KdanAutoTest/KdanAuto/Base.lproj/Main.storyboard

@@ -221,7 +221,7 @@
         <scene sceneID="R2V-B0-nI4">
             <objects>
                 <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
-                    <window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" titleVisibility="hidden" id="IQv-IB-iLA">
+                    <window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
                         <windowStyleMask key="styleMask" titled="YES" miniaturizable="YES"/>
                         <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
                         <rect key="contentRect" x="196" y="240" width="900" height="600"/>

+ 28 - 1
KdanAutoTest/KdanAuto/Class/AutoTestCase/AutoTest.swift

@@ -22,6 +22,14 @@ class AutoTest : NSObject, AutoTestProtocal {
     var _name : String = "对照测试"
     var _params : NSDictionary = [:]
     
+    var testFiles : NSArray = NSArray()
+    var convertProgress : Double = 0.0
+    var compareProgress : Double = 0.0
+    
+    var testlog : (_ msg:String, _ progress:Double) -> () = {(msg, progress) in
+        
+    }
+    
     class func autoTestFor(_ fileType:NSString ,type:NSString) -> AutoTest? {
         let key = String(fileType) + "." + String(type)
         
@@ -160,6 +168,7 @@ class AutoTest : NSObject, AutoTestProtocal {
                 self.reportString = NSMutableAttributedString.init(string: "\n【\(String(self.fileType())) - \(self.name())】快照比对开始!\n",
                                                               attributes:[.foregroundColor : NSColor.blue])
                 let files = DataModel.shared.originFilesFor(self.fileType(), type: self.type()) as [String]
+                self.testFiles = NSArray(array: files);
                 
                 let checkDirectory = self.checkFileDirectory()
                 let originDirectory = self.originFileDirectory()
@@ -201,6 +210,11 @@ class AutoTest : NSObject, AutoTestProtocal {
 //                                                                            attributes:[.foregroundColor : NSColor.blue]))
                         // ...
                         // 执行异步转换过程
+                        let index = self.testFiles.index(of: fileName);
+                        if (index != NSNotFound) {
+                            self.convertProgress = Double(index) / Double(self.testFiles.count)
+                        }
+                        self.testlog("开始转换:"+fileName, (self.compareProgress + self.convertProgress)/2.0)
                         self.process(originPath, resultPath: resultPath) { status in
                             DispatchQueue.global().async {
                                 Thread.sleep(forTimeInterval: 0.3)
@@ -271,7 +285,8 @@ class AutoTest : NSObject, AutoTestProtocal {
     }
     
     func compareResult(_ fileName:String, resultPath:String, status:Int, needCompare:Bool, complention:@escaping (_ degree:Double) -> ()) {
-        NSLog("开始识别:\(fileName)");
+        NSLog("开始识别:\(fileName)")
+        self.testlog("开始对比:"+fileName, (self.convertProgress + self.compareProgress)/2.0)
         let checkDirectory = self.checkFileDirectory()
 //        let originDirectory = self.originFileDirectory()
         let resultDirectory = self.resultFileDirectory()
@@ -320,6 +335,12 @@ class AutoTest : NSObject, AutoTestProtocal {
                                     // 执行下一个文件转档
                                     complention(subDegree)
                                     NSLog("结束识别:\(fileName)");
+                                    
+                                    let index = self.testFiles.index(of: fileName);
+                                    if (index != NSNotFound) {
+                                        self.compareProgress = Double(index) / Double(self.testFiles.count)
+                                    }
+                                    self.testlog("对比完成:"+fileName, (self.compareProgress + self.convertProgress)/2.0)
                                 }
                             }
 //                        }
@@ -468,6 +489,12 @@ class AutoTest : NSObject, AutoTestProtocal {
             autoreleasepool {
                 // 执行下一个文件转档
                 NSLog("结束识别:\(fileName)");
+                
+                let index = self.testFiles.index(of: fileName);
+                if (index != NSNotFound) {
+                    self.compareProgress = Double(index) / Double(self.testFiles.count)
+                }
+                self.testlog("对比完成:"+fileName, (self.compareProgress + self.convertProgress)/2.0)
                 complention(0)
             }
         }

+ 1 - 1
KdanAutoTest/KdanAuto/Class/AutoTestCase/AutoTestProperty.plist

@@ -428,7 +428,7 @@
 			<key>Extention</key>
 			<string>tga</string>
 			<key>Name</key>
-			<string>快照对比(Old)</string>
+			<string>快照对比(老库)</string>
 			<key>Class</key>
 			<string>AutoTest</string>
 			<key>Params</key>

+ 1 - 0
KdanAutoTest/KdanAuto/Class/AutoTestCase/AutoTestProtocal.swift

@@ -59,6 +59,7 @@ protocol AutoTestProtocal : NSObjectProtocol {
     func canUpdateRefImage(_ fileName:String) -> Bool
     func updateRefImage(_ fileName:String)
     
+    var testlog : (_ msg:String, _ progress:Double) -> () { get  }
     
     func status() -> AutoTestStatus
 }

+ 21 - 0
KdanAutoTest/KdanAuto/Class/Norrmal/AutoTestAdvanceSettingView.swift

@@ -417,4 +417,25 @@ class AutoTestAdvanceSettingView : NSView, NSTableViewDataSource, NSTableViewDel
             compareVC.showIn(self, rect: NSRect.init(origin: point, size: sender.frame.size))
         }
     }
+    
+    func fileCellNeedShowInFinder(_ cell:TestFileCellView, fileName:String) {
+        let originDirectory = _autoTestObj?.originFileDirectory()
+        let path = NSString(string: originDirectory!).appendingPathComponent(fileName);
+        
+        NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: path)])
+    }
+    
+    func fileCellNeedDelete(_ cell:TestFileCellView, fileName:String) {
+        let originDirectory = _autoTestObj?.originFileDirectory()
+        let path = NSString(string: originDirectory!).appendingPathComponent(fileName);
+        
+        try? FileManager.default.removeItem(atPath: path);
+        
+        let index = _files.firstIndex(of: fileName)
+        
+        if (index != NSNotFound) {
+            _files.remove(at: index!)
+            _fileList.reloadData()
+        }
+    }
 }

+ 36 - 1
KdanAutoTest/KdanAuto/Class/Norrmal/Cell/TestFileCellView.swift

@@ -12,9 +12,13 @@ protocol TestFileCellViewDelegate : NSObjectProtocol {
     func fileCellNeedReplace(_ cell:TestFileCellView, fileName:String)
     
     func fileCellNeedShowReport(_ cell:TestFileCellView, fileName:String, sender:NSButton)
+    
+    func fileCellNeedShowInFinder(_ cell:TestFileCellView, fileName:String)
+    
+    func fileCellNeedDelete(_ cell:TestFileCellView, fileName:String)
 }
 
-class TestFileCellView : NSTableCellView {
+class TestFileCellView : NSTableCellView, NSMenuDelegate {
     @IBOutlet var _titleLbl : NSTextField!
     @IBOutlet var _sepLine : NSView!
     
@@ -46,6 +50,25 @@ class TestFileCellView : NSTableCellView {
     override func awakeFromNib() {
         _sepLine.wantsLayer = true;
         _sepLine.layer?.backgroundColor = NSColor.lightGray.cgColor
+        
+        let _menu = NSMenu(title: "")
+        let item = NSMenuItem.init(title: "在访达中显示", action:#selector(self.showInFinder(_:)), keyEquivalent: "")
+        item.target = self;
+        _menu.addItem(item)
+        let item1 = NSMenuItem.init(title: "删除", action: #selector(self.deleteActions(_:)), keyEquivalent: "")
+        _menu.addItem(item1)
+        item1.target = self;
+        self.menu = _menu
+        self.menu?.delegate = self;
+        
+        NSEvent.addLocalMonitorForEvents(matching: NSEvent.EventTypeMask.rightMouseUp) { event in
+            if (event.type == .rightMouseUp) {
+                NSMenu.popUpContextMenu(self.menu!, with: event, for: self)
+                return nil;
+            }
+            
+            return event
+        }
     }
     
     // Setter & Getter
@@ -105,4 +128,16 @@ class TestFileCellView : NSTableCellView {
             _delegate?.fileCellNeedShowReport(self, fileName: _title, sender: sender)
         }
     }
+    
+    @IBAction func showInFinder(_ sender:NSMenuItem) {
+        if _delegate != nil {
+            _delegate?.fileCellNeedShowInFinder(self, fileName: _title)
+        }
+    }
+    
+    @IBAction func deleteActions(_ sender:NSMenuItem) {
+        if _delegate != nil {
+            _delegate?.fileCellNeedDelete(self, fileName: _title)
+        }
+    }
 }

+ 71 - 23
KdanAutoTest/KdanAuto/Class/Tools/ImageProcess/ImageProcess.swift

@@ -8,6 +8,7 @@
 import Foundation
 import AppKit
 import CoreImage
+import CryptoKit
 
 class ImageProcess : NSObject {
     
@@ -219,8 +220,13 @@ class ImageProcess : NSObject {
                 return
             }
             
+            if (ImageProcess.checkEqualFor(resultPath, checkPath: checkPath)) {
+                complention(1)
+                return
+            }
+            
             let resultImage = rImage!
-            var checkImage = cImage!
+            let checkImage = cImage!
             
             var resultImageRep = NSBitmapImageRep.init(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
             let checkImageRep = NSBitmapImageRep.init(cgImage: checkImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
@@ -351,29 +357,31 @@ class ImageProcess : NSObject {
             let outDegree = Double(max(equalCount-bgCount, 1)/(max(Double(cWidth) * Double(cHeight)-bgCount, 1)) * 100.0)
             if (abs(outDegree - 100) > 0 && processCover && nil != data) {
                 DispatchQueue.global().async {
-                    let cfData = CFDataCreate(kCFAllocatorDefault, data?.bytes, data!.length)
-                    let dataProvider = CGDataProvider.init(data: cfData!)
-                    
-                    let colorSpace = CGColorSpaceCreateDeviceRGB()
-                    let cgImage = CGImage.init(width: cWidth,
-                                               height: cHeight,
-                                               bitsPerComponent: 8,
-                                               bitsPerPixel: 8 * 4,
-                                               bytesPerRow: cWidth * 4,
-                                               space: colorSpace,
-                                               bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrderDefault.rawValue) ?? CGBitmapInfo.byteOrderDefault,
-                                               provider: dataProvider!,
-                                               decode: nil,
-                                               shouldInterpolate: true,
-                                               intent: CGColorRenderingIntent.defaultIntent)
-                    
-                    if nil != cgImage {
-                        let coverPath = NSString(format: "%@_cover.png", NSString(string: resultPath).deletingPathExtension) as! String
-                        let rep = NSBitmapImageRep.init(cgImage: cgImage!)
+                    autoreleasepool {
+                        let cfData = CFDataCreate(kCFAllocatorDefault, data?.bytes, data!.length)
+                        let dataProvider = CGDataProvider.init(data: cfData!)
                         
-                        if let saveData = rep.representation(using: .png, properties: [:]) {
-                            let url = URL.init(fileURLWithPath: coverPath, isDirectory: false)
-                            try? saveData.write(to: url)
+                        let colorSpace = CGColorSpaceCreateDeviceRGB()
+                        let cgImage = CGImage.init(width: cWidth,
+                                                   height: cHeight,
+                                                   bitsPerComponent: 8,
+                                                   bitsPerPixel: 8 * 4,
+                                                   bytesPerRow: cWidth * 4,
+                                                   space: colorSpace,
+                                                   bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrderDefault.rawValue) ?? CGBitmapInfo.byteOrderDefault,
+                                                   provider: dataProvider!,
+                                                   decode: nil,
+                                                   shouldInterpolate: true,
+                                                   intent: CGColorRenderingIntent.defaultIntent)
+                        
+                        if nil != cgImage {
+                            let coverPath = NSString(format: "%@_cover.png", NSString(string: resultPath).deletingPathExtension) as! String
+                            let rep = NSBitmapImageRep.init(cgImage: cgImage!)
+                            
+                            if let saveData = rep.representation(using: .png, properties: [:]) {
+                                let url = URL.init(fileURLWithPath: coverPath, isDirectory: false)
+                                try? saveData.write(to: url)
+                            }
                         }
                     }
                 }
@@ -401,6 +409,10 @@ class ImageProcess : NSObject {
                 return nil
             }
             
+            if (ImageProcess.checkEqualFor(resultPath, checkPath: checkPath)) {
+                return nil
+            }
+            
             let resultImage = rImage as! NSImage
             let checkImage = cImage as! NSImage
             
@@ -525,4 +537,40 @@ class ImageProcess : NSObject {
             return nil
         }
     }
+    
+    class func checkEqualFor(_ resultPath:String, checkPath:String) -> Bool {
+        // 先通过文件属性 + MD5 码进行快速对比,排除大部分,已经相同的文件
+        let rfs = try! FileManager.default.attributesOfItem(atPath: resultPath)[FileAttributeKey.size] as! NSNumber
+        let cfs = try! FileManager.default.attributesOfItem(atPath: checkPath)[FileAttributeKey.size] as! NSNumber
+        if  rfs.int64Value != cfs.int64Value {
+            //文件大小不一致,反回不一致
+            return false
+        }
+        
+        let rmd5 = ImageProcess.md5StringOfPath(resultPath);
+        let cmd5 = ImageProcess.md5StringOfPath(checkPath);
+        
+        if (nil != rmd5 && nil != cmd5 &&
+            NSString(string: rmd5!).isEqual(to: cmd5!)) {
+            // 文件大小一致,且文件 MD5码完全一致
+            return true;
+        }
+        
+        return false
+    }
+    
+    class func md5StringOfPath(_ path:String) -> String? {
+        if FileManager.default.fileExists(atPath: path) {
+            let data = NSData(contentsOfFile: path);
+            
+            if (nil == data) {
+                return nil;
+            }
+            
+            return String(format: "%x", data.hashValue);
+        }
+        
+        return nil;
+    }
+    
 }

+ 7 - 0
KdanAutoTest/KdanAuto/ViewController.swift

@@ -146,6 +146,11 @@ class ViewController : NSViewController, SettingViewControllerDelegate, AutoTest
                     let type = ti["Type"] as! NSString
                     
                     let testObject = AutoTest.autoTestFor(fileType as NSString, type: type)
+                    testObject?.testlog = { (msg, progress) in
+                        DispatchQueue.main.async {
+                            self.view.window?.title = "\(msg)(\(Int(progress*100))%)";
+                        }
+                    }
                     if (testObject != nil) {
                         objects.add(testObject);
                     }
@@ -176,6 +181,8 @@ class ViewController : NSViewController, SettingViewControllerDelegate, AutoTest
                     }
                     
                     DispatchQueue.main.async {
+                        self.view.window?.title = "";
+                        
                         self._isProcessing = false
                         self.updateProcessStatus()
                         self.startBtn.isEnabled = true

+ 9 - 3
KdanAutoTest/ProcessCheckFile/Process/ProcessThumbnal.swift

@@ -63,12 +63,16 @@ class ProcessThumbnal : NSObject {
         }
         
         var didFinished = false
+        var retryCount = 0;
         var didCallback = false;
         
         let url = URL.init(fileURLWithPath: filePath, isDirectory: false)
         let request = QLThumbnailGenerator.Request.init(fileAt: url, size: outputSize, scale: 2.0, representationTypes: QLThumbnailGenerator.Request.RepresentationTypes.thumbnail)
         generate.generateBestRepresentation(for: request,
                                             completion: { (representation, error) in
+            if (error != nil) {
+                NSLog("\(error)");
+            }
             if nil != representation {
                 autoreleasepool {
                     let image = representation!.nsImage as NSImage
@@ -85,12 +89,14 @@ class ProcessThumbnal : NSObject {
                 }
             }
             
-            if (!didCallback) {
+            if (!didCallback && (didFinished || retryCount > 2)) {
                 didCallback = true;
                 complention(didFinished)
+                
+                generate.cancel(request)
+            }else {
+                retryCount += 1;
             }
-            
-            generate.cancel(request)
         })
     }