Compatibility with macOS 13.3

This commit is contained in:
Alexander Heinrich
2023-04-21 09:54:51 +02:00
parent 4e0f37d129
commit fe1e286182
11 changed files with 226 additions and 166 deletions

View File

@@ -1,34 +1,32 @@
{
"object": {
"pins": [
{
"package": "swift-crypto",
"repositoryURL": "https://github.com/apple/swift-crypto.git",
"state": {
"branch": null,
"revision": "3bea268b223651c4ab7b7b9ad62ef9b2d4143eb6",
"version": "1.1.6"
}
},
{
"package": "swift-nio",
"repositoryURL": "https://github.com/apple/swift-nio.git",
"state": {
"branch": null,
"revision": "6aa9347d9bc5bbfe6a84983aec955c17ffea96ef",
"version": "2.33.0"
}
},
{
"package": "swift-nio-ssl",
"repositoryURL": "https://github.com/apple/swift-nio-ssl",
"state": {
"branch": null,
"revision": "5e68c1ded15619bb281b273fa8c2d8fd7f7b2b7d",
"version": "2.16.1"
}
"pins" : [
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "ddb07e896a2a8af79512543b1c7eb9797f8898a5",
"version" : "1.1.7"
}
]
},
"version": 1
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "124119f0bb12384cef35aa041d7c3a686108722d",
"version" : "2.40.0"
}
},
{
"identity" : "swift-nio-ssl",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-ssl",
"state" : {
"revision" : "c30c680c78c99afdabf84805a83c8745387c4ac7",
"version" : "2.20.2"
}
}
],
"version" : 2
}

View File

@@ -98,7 +98,8 @@ public class AnisetteDataManager: NSObject {
"X-Apple-I-MD-M": data.machineID,
"X-Apple-I-MD": data.oneTimePassword,
"X-Apple-I-TimeZone": String(data.timeZone.abbreviation() ?? "UTC"),
"X-Apple-I-Client-Time": ISO8601DateFormatter().string(from: data.date),
// "X-Apple-I-Client-Time": ISO8601DateFormatter().string(from: data.date),
"X-Apple-I-Client-Time": ISO8601DateFormatter().string(from: Date()),
"X-Apple-I-MD-RINFO": String(data.routingInfo),
] as [AnyHashable: Any])
}

View File

@@ -17,7 +17,7 @@ class AccessoryController: ObservableObject {
var selfObserver: AnyCancellable?
var listElementsObserver = [AnyCancellable]()
let findMyController: FindMyController
weak var savePanel: NSSavePanel?
init(accessories: [Accessory], findMyController: FindMyController) {
@@ -99,13 +99,13 @@ class AccessoryController: ObservableObject {
func export(accessories: [Accessory]) throws -> URL {
let savePanel = NSSavePanel()
// savePanel.allowedFileTypes = ["plist", "json"]
// savePanel.allowedFileTypes = ["plist", "json"]
if #available(macOS 12.0, *) {
savePanel.allowedContentTypes = [.propertyList]
}else {
} else {
savePanel.allowedFileTypes = ["plist"]
}
savePanel.canCreateDirectories = true
savePanel.directoryURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
savePanel.message = "This export contains all private keys! Keep the file save to protect your location data"
@@ -114,7 +114,7 @@ class AccessoryController: ObservableObject {
savePanel.prompt = "Export"
savePanel.title = "Export accessories & keys"
savePanel.isExtensionHidden = false
let accessoryView = NSView()
let popUpButton = NSPopUpButton(title: "File type", target: self, action: #selector(exportFileTypeChanged(button:)))
popUpButton.addItems(withTitles: ["Property List", "JSON"])
@@ -122,23 +122,23 @@ class AccessoryController: ObservableObject {
popUpButton.stringValue = "File type"
popUpButton.translatesAutoresizingMaskIntoConstraints = false
accessoryView.addSubview(popUpButton)
let popUpButtonLabel = NSTextField(labelWithString: "File type")
popUpButtonLabel.translatesAutoresizingMaskIntoConstraints = false
accessoryView.addSubview(popUpButtonLabel)
accessoryView.translatesAutoresizingMaskIntoConstraints = false
// popUpButtonLabel.leadingAnchor.constraint(greaterThanOrEqualTo: accessoryView.leadingAnchor, constant: 20.0).isActive = true
// popUpButtonLabel.leadingAnchor.constraint(greaterThanOrEqualTo: accessoryView.leadingAnchor, constant: 20.0).isActive = true
popUpButtonLabel.trailingAnchor.constraint(equalTo: popUpButton.leadingAnchor, constant: -8.0).isActive = true
popUpButtonLabel.trailingAnchor.constraint(lessThanOrEqualTo: accessoryView.centerXAnchor, constant: 0).isActive = true
popUpButtonLabel.centerYAnchor.constraint(equalTo: popUpButton.centerYAnchor, constant: 0).isActive = true
// popUpButton.trailingAnchor.constraint(lessThanOrEqualTo: accessoryView.trailingAnchor, constant: -20.0).isActive = true
// popUpButton.trailingAnchor.constraint(lessThanOrEqualTo: accessoryView.trailingAnchor, constant: -20.0).isActive = true
popUpButton.leadingAnchor.constraint(lessThanOrEqualTo: accessoryView.centerXAnchor, constant: 0).isActive = true
popUpButton.topAnchor.constraint(equalTo: accessoryView.topAnchor, constant: 8.0).isActive = true
popUpButton.bottomAnchor.constraint(equalTo: accessoryView.bottomAnchor, constant: -8.0).isActive = true
popUpButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 20.0).isActive = true
popUpButton.widthAnchor.constraint(lessThanOrEqualToConstant: 200.0).isActive = true
savePanel.accessoryView = accessoryView
self.savePanel = savePanel
@@ -148,7 +148,7 @@ class AccessoryController: ObservableObject {
var url = savePanel.url
{
let selectedItemIndex = popUpButton.indexOfSelectedItem
// Store the accessory file
if selectedItemIndex == 0 {
if url.pathExtension != "plist" {
@@ -156,7 +156,7 @@ class AccessoryController: ObservableObject {
}
let propertyList = try PropertyListEncoder().encode(accessories)
try propertyList.write(to: url)
}else if selectedItemIndex == 1 {
} else if selectedItemIndex == 1 {
if url.pathExtension != "json" {
url = url.appendingPathExtension("json")
}
@@ -168,18 +168,18 @@ class AccessoryController: ObservableObject {
}
throw ImportError.cancelled
}
@objc func exportFileTypeChanged(button: NSPopUpButton) {
if button.indexOfSelectedItem == 0 {
if #available(macOS 12.0, *) {
self.savePanel?.allowedContentTypes = [.propertyList]
}else {
} else {
self.savePanel?.allowedFileTypes = ["plist"]
}
}else {
} else {
if #available(macOS 12.0, *) {
self.savePanel?.allowedContentTypes = [.json]
}else {
} else {
self.savePanel?.allowedFileTypes = ["json"]
}
}
@@ -190,10 +190,10 @@ class AccessoryController: ObservableObject {
let openPanel = NSOpenPanel()
if #available(macOS 12.0, *) {
openPanel.allowedContentTypes = [.json, .propertyList]
}else {
} else {
openPanel.allowedFileTypes = ["json", "plist"]
}
openPanel.canCreateDirectories = true
openPanel.directoryURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
openPanel.message = "Import an accessories file that includes the private keys"
@@ -208,10 +208,10 @@ class AccessoryController: ObservableObject {
var importedAccessories: [Accessory]
if url.pathExtension == "plist" {
importedAccessories = try PropertyListDecoder().decode([Accessory].self, from: accessoryData)
}else {
} else {
importedAccessories = try JSONDecoder().decode([Accessory].self, from: accessoryData)
}
var updatedAccessories = self.accessories
// Filter out accessories with the same id (no duplicates)
importedAccessories = importedAccessories.filter({ acc in !self.accessories.contains(where: { acc.id == $0.id }) })

View File

@@ -10,7 +10,6 @@
import CoreLocation
import Foundation
import SwiftUI
import CoreLocation
// swiftlint:disable force_try
struct PreviewData {

View File

@@ -7,83 +7,83 @@
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
import AppKit
import Foundation
/// Can check if a new OpenHaystack version is needed and download it.
public struct UpdateCheckController {
public static func checkForNewVersion() {
// Load the GitHub Releases page
let releasesURL = URL(string: "https://github.com/seemoo-lab/openhaystack/releases")!
URLSession.shared.dataTask(with: releasesURL) { optionalData, response, error in
guard let data = optionalData,
(response as? HTTPURLResponse)?.statusCode == 200,
let htmlString = String(data:data, encoding: .utf8)
(response as? HTTPURLResponse)?.statusCode == 200,
let htmlString = String(data: data, encoding: .utf8)
else {
return
}
guard let availableVersion = getVersion(from: htmlString) else {
return
}
//Get installed version
let version = Bundle.main.infoDictionary?["CFBundleVersionShortString"] as? String ?? "0"
let comparisonResult = compareVersions(availableVersion: availableVersion, installedVersion: version)
DispatchQueue.main.async {
if comparisonResult == .older, askToDownloadUpdate() == .alertSecondButtonReturn {
//The currently installed version is older. Install an update
self.downloadUpdate(version: availableVersion, finished: { success in
if success {
let result = successDownloadAlert()
if result == .alertSecondButtonReturn {
//Open the download folder
let downloadURL = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
NSWorkspace.shared.open(downloadURL)
self.downloadUpdate(
version: availableVersion,
finished: { success in
if success {
let result = successDownloadAlert()
if result == .alertSecondButtonReturn {
//Open the download folder
let downloadURL = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
NSWorkspace.shared.open(downloadURL)
}
} else {
if downloadFailedAlert() == .alertSecondButtonReturn {
NSWorkspace.shared.open(URL(string: "https://github.com/seemoo-lab/openhaystack/releases")!)
}
}
}else {
if downloadFailedAlert() == .alertSecondButtonReturn {
NSWorkspace.shared.open(URL(string: "https://github.com/seemoo-lab/openhaystack/releases")!)
}
}
})
})
}
}
}.resume()
}
internal static func getVersion(from htmlString: String) -> String? {
guard let regex = try? NSRegularExpression(pattern: "Release (v[0-9]+(.[0-9]+)?(.[0-9]+)?)") else {
return nil
}
let htmlNSString = htmlString as NSString
let htmlRange = NSRange(location: 0, length: htmlNSString.length)
if let checkResult = regex.firstMatch(in: htmlNSString as String, options: [], range: htmlRange),
checkResult.numberOfRanges >= 2 {
checkResult.numberOfRanges >= 2
{
//Get the latest release version range
// A result should have multiple ranges for each capture group. 1 is the capture group for the version number
let releaseVersionRange = checkResult.range(at: 1)
let releaseVersion = htmlNSString.substring(with: releaseVersionRange)
let releaseVersionNumber = releaseVersion.replacingOccurrences(of: "v", with: "")
return releaseVersionNumber
}
return nil
}
/// Compares two version strings and returns if the installed version is older, newer or the same
/// - Parameters:
/// - availableVersion: The latest available version
@@ -92,110 +92,110 @@ public struct UpdateCheckController {
internal static func compareVersions(availableVersion: String, installedVersion: String) -> VersionCompare {
let availableVersionSplit = availableVersion.split(separator: ".")
let installedVersionSplit = installedVersion.split(separator: ".")
for (idx, availableVersionPart) in availableVersionSplit.enumerated() {
if idx < installedVersionSplit.count {
guard let avpi = Int(availableVersionPart),
let ivpi = Int(installedVersionSplit[idx]) else {return .older}
let ivpi = Int(installedVersionSplit[idx])
else { return .older }
if avpi > ivpi {
return .older
}else if ivpi > avpi {
} else if ivpi > avpi {
return .newer
}
}else {
} else {
//The installed version is x.x
// The new version is x.x.y so it must be older
return .older
}
}
if installedVersionSplit.count > availableVersionSplit.count {
//The installed version has a higher sub-version. So it must be newer
return .newer
}
// All numbers were equal
return .same
}
enum VersionCompare {
case same, newer, older
}
static func downloadUpdate(version: String, finished: @escaping (Bool)->()) {
static func downloadUpdate(version: String, finished: @escaping (Bool) -> Void) {
//Download the current version into a file in Downloads
let downloadURL = URL(string: "https://github.com/seemoo-lab/openhaystack/releases/download/v\(version)/OpenHaystack.zip")!
let task = URLSession.shared.downloadTask(with: downloadURL) { optionalFileURL, response, error in
guard let downloadLocation = optionalFileURL else {
finished(false)
return
}
//Move the file to the downloads folder
let downloadURL = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
let openHaystackURL = downloadURL.appendingPathComponent("OpenHaystack.zip")
do {
let fm = FileManager.default
if fm.fileExists(atPath: openHaystackURL.path) {
_ = try fm.replaceItemAt(openHaystackURL, withItemAt: downloadLocation)
}else {
try fm.moveItem(at: downloadLocation, to: openHaystackURL)
}
DispatchQueue.main.async {finished(true)}
}catch let error {
print(error.localizedDescription)
DispatchQueue.main.async {finished(false)}
guard let downloadLocation = optionalFileURL else {
finished(false)
return
}
//Move the file to the downloads folder
let downloadURL = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
let openHaystackURL = downloadURL.appendingPathComponent("OpenHaystack.zip")
do {
let fm = FileManager.default
if fm.fileExists(atPath: openHaystackURL.path) {
_ = try fm.replaceItemAt(openHaystackURL, withItemAt: downloadLocation)
} else {
try fm.moveItem(at: downloadLocation, to: openHaystackURL)
}
DispatchQueue.main.async { finished(true) }
} catch let error {
print(error.localizedDescription)
DispatchQueue.main.async { finished(false) }
}
}
task.resume()
}
private static func askToDownloadUpdate() -> NSApplication.ModalResponse {
let alert = NSAlert()
alert.messageText = NSLocalizedString("New version available", comment: "Alert title")
alert.informativeText = NSLocalizedString("A new version of OpenHaystack is available. Do you want to download it now?", comment: "Alert text")
alert.addButton(withTitle: "Cancel")
alert.addButton(withTitle: "Download")
return alert.runModal()
}
private static func successDownloadAlert() -> NSApplication.ModalResponse {
let alert = NSAlert()
alert.messageText = NSLocalizedString("Successfully downloaded update", comment: "Alert title")
alert.informativeText = NSLocalizedString("The new version has been downloaded successfully and it was placed in your Downloads folder.", comment: "Alert text")
alert.addButton(withTitle: "Okay")
alert.addButton(withTitle: "Open folder")
return alert.runModal()
}
private static func downloadFailedAlert() -> NSApplication.ModalResponse {
let alert = NSAlert()
alert.messageText = NSLocalizedString("Download failed", comment: "Alert title")
alert.informativeText = NSLocalizedString("To update to the newest version, please open the releases page on GitHub", comment: "Alert text")
alert.addButton(withTitle: "Cancel")
alert.addButton(withTitle: "Open")
return alert.runModal()
}
}
extension String {
func substring(from range: NSRange) -> String {
let substring = self[self.index(startIndex, offsetBy: range.lowerBound)..<self.index(startIndex, offsetBy: range.upperBound)]
return String(substring)
}
}

View File

@@ -98,8 +98,13 @@ struct AccessoryListEntry: View {
}
Divider()
Button("Mark as \(accessory.isDeployed ? "deployable" : "deployed")", action: { accessory.isDeployed.toggle() })
Button("Copy private Key B64", action: { copyPrivateKey(accessory: accessory) })
Group {
Button("Copy private Key B64", action: { copyPrivateKey(accessory: accessory) })
Button("Export Locations", action: { exportLocations(accessory: accessory) })
}
}
}
@@ -190,16 +195,27 @@ struct AccessoryListEntry: View {
assert(false)
}
}
func copyPrivateKey(accessory: Accessory) {
let privateKey = accessory.privateKey
let keyB64 = privateKey.base64EncodedString()
let pasteboard = NSPasteboard.general
pasteboard.prepareForNewContents(with: .currentHostOnly)
pasteboard.setString(keyB64, forType: .string)
}
func exportLocations(accessory: Accessory) {
guard let locations = accessory.locations,
let locationData = try? JSONEncoder().encode(locations)
else {
return
}
let savePanel = SavePanel.shared
savePanel.saveFile(file: locationData, fileExtension: "json")
}
struct AccessoryListEntry_Previews: PreviewProvider {
@StateObject static var accessory = PreviewData.accessories.first!
@State static var alertType: OpenHaystackMainView.AlertType?

View File

@@ -11,7 +11,7 @@ import AppKit
import Foundation
import SwiftUI
final class ActivityIndicator: NSViewRepresentable {
struct ActivityIndicator: NSViewRepresentable {
init(size: NSControl.ControlSize) {
self.size = size

View File

@@ -43,9 +43,11 @@ struct NRFInstallSheet: View {
Divider()
Text("The new NRF firmware uses rotating keys. This means that the device changes its public key after a specific number of days. This disallows ad networks to track your device over several days when you are moving around the city. Shorter update cycles then days are not supported")
Text(
"The new NRF firmware uses rotating keys. This means that the device changes its public key after a specific number of days. This disallows ad networks to track your device over several days when you are moving around the city. Shorter update cycles then days are not supported"
)
self.timePicker
Text("One day is a reasonable amount of time")
.font(.footnote)
.foregroundColor(.secondary)
@@ -82,7 +84,7 @@ struct NRFInstallSheet: View {
self.presentationMode.wrappedValue.dismiss()
})
}
HStack {
Spacer()
Text("Flashing from M1 Macs might fail due to missing ARM support by NRF")

View File

@@ -15,7 +15,7 @@ struct OpenHaystackApp: App {
var accessoryNearbyMonitor: AccessoryNearbyMonitor?
var frameWidth: CGFloat? = nil
var frameHeight: CGFloat? = nil
@State var checkedForUpdates = false
init() {
@@ -45,9 +45,9 @@ struct OpenHaystackApp: App {
SidebarCommands()
}
}
func checkForUpdates() {
guard checkedForUpdates == false, ProcessInfo().arguments.contains("-stopUpdateCheck") == false else {return}
guard checkedForUpdates == false, ProcessInfo().arguments.contains("-stopUpdateCheck") == false else { return }
UpdateCheckController.checkForNewVersion()
checkedForUpdates = true
}

View File

@@ -178,5 +178,48 @@
<string>224E7F96-2099-499C-A501-63FB68C79CD2</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported13.0PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>224E7F96-2099-499C-A501-63FB68C79CD2</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported13.1PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>224E7F96-2099-499C-A501-63FB68C79CD2</string>
<string>890E3F5B-9490-4828-8F3F-B6561E513FCC</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
<string>281F8A5C-0AF9-4BE6-8B8A-C0CB9C2068BE</string>
</array>
<key>Supported13.2PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>890E3F5B-9490-4828-8F3F-B6561E513FCC</string>
<string>224E7F96-2099-499C-A501-63FB68C79CD2</string>
<string>281F8A5C-0AF9-4BE6-8B8A-C0CB9C2068BE</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported13.3PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>890E3F5B-9490-4828-8F3F-B6561E513FCC</string>
<string>224E7F96-2099-499C-A501-63FB68C79CD2</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
<string>281F8A5C-0AF9-4BE6-8B8A-C0CB9C2068BE</string>
</array>
<key>Supported13.4PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>224E7F96-2099-499C-A501-63FB68C79CD2</string>
<string>890E3F5B-9490-4828-8F3F-B6561E513FCC</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
<string>281F8A5C-0AF9-4BE6-8B8A-C0CB9C2068BE</string>
</array>
</dict>
</plist>

View File

@@ -9,46 +9,47 @@
import Foundation
import XCTest
@testable import OpenHaystack
class UpdateCheckTests: XCTestCase {
func testCompareVersions() {
let i1 = "1.0.3"
let a1 = "1.0.4"
XCTAssertEqual(UpdateCheckController.compareVersions(availableVersion: a1, installedVersion: i1), .older)
let a11 = "1.1"
XCTAssertEqual(UpdateCheckController.compareVersions(availableVersion: a11, installedVersion: i1), .older)
let a12 = "2"
let a12 = "2"
XCTAssertEqual(UpdateCheckController.compareVersions(availableVersion: a12, installedVersion: i1), .older)
let a2 = "1.0.3"
XCTAssertEqual(UpdateCheckController.compareVersions(availableVersion: a2, installedVersion: i1), .same)
let a3 = "1.0.2"
XCTAssertEqual(UpdateCheckController.compareVersions(availableVersion: a3, installedVersion: i1), .newer)
let a31 = "1.0"
XCTAssertEqual(UpdateCheckController.compareVersions(availableVersion: a31, installedVersion: i1), .newer)
let a32 = "0.10.1"
XCTAssertEqual(UpdateCheckController.compareVersions(availableVersion: a32, installedVersion: i1), .newer)
let a4 = "1.1.1"
let i4 = "1.1.2"
XCTAssertEqual(UpdateCheckController.compareVersions(availableVersion: a4, installedVersion: i4), .newer)
let a41 = "1.0.2"
XCTAssertEqual(UpdateCheckController.compareVersions(availableVersion: a41, installedVersion: i1), .newer)
}
func testHTMLVersionCompare() {
let github =
"""
<h1 data-view-component="true" class="d-inline mr-3"><a href="/seemoo-lab/openhaystack/releases/tag/v0.4.1" data-view-component="true" class="Link--primary">Release v0.4.1</a></h1>
<h1 data-view-component="true" class="d-inline mr-3"><a href="/seemoo-lab/openhaystack/releases/tag/v0.4.1" data-view-component="true" class="Link--primary">Release v0.4.1</a></h1>
<a href="/seemoo-lab/openhaystack/releases/tag/v0.4.1" data-view-component="true" class="Link--primary">Release v0.4.1</a>
"""
"""
<h1 data-view-component="true" class="d-inline mr-3"><a href="/seemoo-lab/openhaystack/releases/tag/v0.4.1" data-view-component="true" class="Link--primary">Release v0.4.1</a></h1>
<h1 data-view-component="true" class="d-inline mr-3"><a href="/seemoo-lab/openhaystack/releases/tag/v0.4.1" data-view-component="true" class="Link--primary">Release v0.4.1</a></h1>
<a href="/seemoo-lab/openhaystack/releases/tag/v0.4.1" data-view-component="true" class="Link--primary">Release v0.4.1</a>
"""
XCTAssertEqual(UpdateCheckController.getVersion(from: github), "0.4.1")
let h1 = "<h1>Release v0.4.1</h1> <h1>Release v0.3.1</h1>"
XCTAssertEqual(UpdateCheckController.getVersion(from: h1), "0.4.1")
let h2 = "<h1>Release v0.5</h1>"
@@ -58,16 +59,16 @@ class UpdateCheckTests: XCTestCase {
let h4 = "<h1>Release v1</h1>"
XCTAssertEqual(UpdateCheckController.getVersion(from: h4), "1")
}
func testDownload() {
let expect = expectation(description: "Update download")
UpdateCheckController.downloadUpdate(version: "0.4.1", finished: { success in
XCTAssertTrue(success)
expect.fulfill()
})
UpdateCheckController.downloadUpdate(
version: "0.4.1",
finished: { success in
XCTAssertTrue(success)
expect.fulfill()
})
wait(for: [expect], timeout: 20.0)
}
}