1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-11-07 05:10:06 +01:00

♻️ Migrate more tests to Swift Testing

This commit is contained in:
2025-09-29 16:31:30 +02:00
parent 9c9720de42
commit 5b27d9f0ea
18 changed files with 330 additions and 364 deletions

View File

@@ -1361,6 +1361,11 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */, C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */,
C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */,
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */,
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */,
C4551656297AED18009B8466 /* ValetRcTest.swift */,
C456A0D02AA6175D0080144F /* Config */,
); );
path = Parsers; path = Parsers;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -2188,11 +2193,6 @@
C4C1019727C65A11001FACC2 /* Parsers */ = { C4C1019727C65A11001FACC2 /* Parsers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C456A0D02AA6175D0080144F /* Config */,
C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */,
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */,
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */,
C4551656297AED18009B8466 /* ValetRcTest.swift */,
C40934AA298EEDA900D25014 /* CaskFileParserTest.swift */, C40934AA298EEDA900D25014 /* CaskFileParserTest.swift */,
C4AFC4B229C4F43300BF4E0D /* HomebrewUpgradableTest.swift */, C4AFC4B229C4F43300BF4E0D /* HomebrewUpgradableTest.swift */,
C4F520662AF03791006787F2 /* ExtensionEnumeratorTest.swift */, C4F520662AF03791006787F2 /* ExtensionEnumeratorTest.swift */,

View File

@@ -26,34 +26,34 @@ struct Constants {
be displayed. This is based on an appropriate launch time on a be displayed. This is based on an appropriate launch time on a
basic M1 Apple chip, with some margin for slower Intel chips. basic M1 Apple chip, with some margin for slower Intel chips.
*/ */
static let SlowBootThresholdInterval: TimeInterval = 30.0 static let SlowBootThresholdInterval: TimeInterval = .seconds(30)
/** /**
The interval between automatic background update checks. The interval between automatic background update checks.
*/ */
static let AutomaticUpdateCheckInterval: TimeInterval = 60.0 * 60 * 24 // 24 hours static let AutomaticUpdateCheckInterval: TimeInterval = .hours(24)
/** /**
The minimum interval that must pass before allowing another The minimum interval that must pass before allowing another
automatic update check. This prevents excessive checking automatic update check. This prevents excessive checking
on frequent app restarts (due to crashes or bad config). on frequent app restarts (due to crashes or bad config).
*/ */
static let MinimumUpdateCheckInterval: TimeInterval = 60.0 * 60 // 60 minutes static let MinimumUpdateCheckInterval: TimeInterval = .minutes(60)
/** /**
Retry intervals for failed automatic update checks. Retry intervals for failed automatic update checks.
Uses exponential backoff before falling back to normal schedule. Uses exponential backoff before falling back to normal schedule.
*/ */
static let UpdateCheckRetryIntervals: [TimeInterval] = [ static let UpdateCheckRetryIntervals: [TimeInterval] = [
60 * 5, // 5 minutes .minutes(5),
60 * 15, // 15 minutes .minutes(15),
60 * 60, // 1 hour .hours(1),
60 * 60 * 3 // 3 hours (final attempt) .hours(3)
] ]
/** /**
PHP Monitor supplies a hardcoded list of PHP packages in its own PHP Monitor supplies a hardcoded list of PHP packages in its own
PHP Version Manager. PHP Version Manager.
This hardcoded list will expire and will need to be modified when This hardcoded list will expire and will need to be modified when
the cutoff date occurs, which is when the `php` formula will the cutoff date occurs, which is when the `php` formula will
@@ -62,8 +62,12 @@ struct Constants {
If users launch an older version of the app, then a warning If users launch an older version of the app, then a warning
will be displayed to let them know that certain operations will be displayed to let them know that certain operations
will not work correctly and that they need to update their app. will not work correctly and that they need to update their app.
The cutoff date is always a few days after GA of the latest
release, as it often takes a while for Homebrew to make the
new release available and not everyone uses a separate tap.
*/ */
static let PhpFormulaeCutoffDate = "2025-11-30" // YYYY-MM-DD static let PhpFormulaeCutoffDate = "2025-11-20" // YYYY-MM-DD
/** /**
* The PHP versions that are considered pre-release versions. * The PHP versions that are considered pre-release versions.
@@ -72,7 +76,8 @@ struct Constants {
*/ */
static var ExperimentalPhpVersions: Set<String> { static var ExperimentalPhpVersions: Set<String> {
let releaseDates = [ let releaseDates = [
"8.5": Date.fromString(Self.PhpFormulaeCutoffDate), // "8.6": Date.fromString("2026-11-30"), // TBD
"8.5": Date.fromString(PhpFormulaeCutoffDate),
"8.4": Date.fromString("2024-11-22") "8.4": Date.fromString("2024-11-22")
] ]
@@ -108,6 +113,7 @@ struct Constants {
"7.0", "7.1", "7.2", "7.3", "7.4", "7.0", "7.1", "7.2", "7.3", "7.4",
"8.0", "8.1", "8.2", "8.3", "8.4", "8.0", "8.1", "8.2", "8.3", "8.4",
"8.5" // DEV "8.5" // DEV
// "8.6" // TBD
] ]
/** /**
@@ -130,48 +136,28 @@ struct Constants {
"7.1", "7.2", "7.3", "7.4", "7.1", "7.2", "7.3", "7.4",
"8.0", "8.1", "8.2", "8.3", "8.4", "8.0", "8.1", "8.2", "8.3", "8.4",
"8.5" // DEV "8.5" // DEV
// "8.6" // TBD
] ]
] ]
struct Urls { struct Urls {
// phpmon.app URLs (these are aliased to redirect correctly) // phpmon.app URLs (these are aliased to redirect correctly)
static let DonationPage = url("https://phpmon.app/sponsor")
static let DonationPage = URL( static let FrequentlyAskedQuestions = url("https://phpmon.app/faq")
string: "https://phpmon.app/sponsor"
)!
static let FrequentlyAskedQuestions = URL( static let WikiPhpUnavailable = url("https://phpmon.app/php-unavailable")
string: "https://phpmon.app/faq"
)!
static let WikiPhpUnavailable = URL( static let WikiPhpUpgrade = url("https://phpmon.app/php-upgrade")
string: "https://phpmon.app/php-unavailable"
)!
static let WikiPhpUpgrade = URL( static let DonationPayment = url("https://phpmon.app/sponsor/now")
string: "https://phpmon.app/php-upgrade"
)!
static let DonationPayment = URL( static let EarlyAccessChangelog = url("https://phpmon.app/early-access/release-notes")
string: "https://phpmon.app/sponsor/now"
)!
static let EarlyAccessChangelog = URL(
string: "https://phpmon.app/early-access/release-notes"
)!
// API endpoints (via api.phpmon.app) // API endpoints (via api.phpmon.app)
static let UpdateCheckEndpoint = url("https://api.phpmon.app/api/v1/update-check")
static let UpdateCheckEndpoint = URL(
string: "https://api.phpmon.app/api/v1/update-check"
)!
// GitHub URLs (do not alias these) // GitHub URLs (do not alias these)
static let GitHubReleases = url("https://github.com/nicoverbruggen/phpmon/releases")
static let GitHubReleases = URL(
string: "https://github.com/nicoverbruggen/phpmon/releases"
)!
} }
} }

View File

@@ -52,7 +52,9 @@ func delay(seconds: Double) async {
try! await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000)) try! await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
} }
/** A simpler way to initialize a fixed, valid URL. */ /**
A simpler way to initialize a fixed, valid URL.
*/
func url(_ string: String) -> URL { func url(_ string: String) -> URL {
return URL(string: string)! return URL(string: string)!
} }

View File

@@ -9,7 +9,14 @@
import Foundation import Foundation
extension TimeInterval { extension TimeInterval {
public static func minutes(_ amount: Int) -> TimeInterval { static func seconds(_ value: Double) -> TimeInterval { value }
return Double(amount * 60) static func minutes(_ value: Double) -> TimeInterval { value * 60 }
static func hours(_ value: Double) -> TimeInterval { value * 3600 }
static func days(_ value: Double) -> TimeInterval { value * 86400 }
}
extension Date {
func adding(_ interval: TimeInterval) -> Date {
return self.addingTimeInterval(interval)
} }
} }

View File

@@ -1,42 +0,0 @@
//
// BytePhpPreferenceTest.swift
// Unit Tests
//
// Created by Nico Verbruggen on 04/09/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import XCTest
class BytePhpPreferenceTest: XCTestCase {
func test_can_extract_memory_value() throws {
let pref = BytePhpPreference(key: "memory_limit")
XCTAssertEqual(pref.internalValue, "512M")
XCTAssertEqual(pref.unit, .megabyte)
XCTAssertEqual(pref.value, 512)
}
func test_can_parse_all_kinds_of_values() throws {
var (unit, value) = BytePhpPreference.readFrom(internalValue: "1G")!
XCTAssertEqual(unit, .gigabyte)
XCTAssertEqual(value, 1)
(unit, value) = BytePhpPreference.readFrom(internalValue: "256M")!
XCTAssertEqual(unit, .megabyte)
XCTAssertEqual(value, 256)
(unit, value) = BytePhpPreference.readFrom(internalValue: "512K")!
XCTAssertEqual(unit, .kilobyte)
XCTAssertEqual(value, 512)
(unit, value) = BytePhpPreference.readFrom(internalValue: "1024")!
XCTAssertEqual(unit, .kilobyte)
XCTAssertEqual(value, 1024)
(unit, value) = BytePhpPreference.readFrom(internalValue: "-1")!
XCTAssertEqual(unit, .kilobyte)
XCTAssertEqual(value, -1)
}
}

View File

@@ -1,81 +0,0 @@
//
// NginxConfigurationTest.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 29/11/2021.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import XCTest
class NginxConfigurationTest: XCTestCase {
// MARK: - Test Files
static var regularUrl: URL {
return Bundle(for: Self.self).url(forResource: "nginx-site", withExtension: "test")!
}
static var isolatedUrl: URL {
return Bundle(for: Self.self).url(forResource: "nginx-site-isolated", withExtension: "test")!
}
static var proxyUrl: URL {
return Bundle(for: Self.self).url(forResource: "nginx-proxy", withExtension: "test")!
}
static var secureProxyUrl: URL {
return Bundle(for: Self.self).url(forResource: "nginx-secure-proxy", withExtension: "test")!
}
static var customTldProxyUrl: URL {
return Bundle(for: Self.self).url(forResource: "nginx-secure-proxy-custom-tld", withExtension: "test")!
}
// MARK: - Tests
func test_can_determine_site_name_and_tld() throws {
XCTAssertEqual(
"nginx-site",
NginxConfigurationFile.from(filePath: NginxConfigurationTest.regularUrl.path)?.domain
)
XCTAssertEqual(
"test",
NginxConfigurationFile.from(filePath: NginxConfigurationTest.regularUrl.path)?.tld
)
}
func test_can_determine_isolation() throws {
XCTAssertNil(
NginxConfigurationFile.from(filePath: NginxConfigurationTest.regularUrl.path)?.isolatedVersion
)
XCTAssertEqual(
"8.1",
NginxConfigurationFile.from(filePath: NginxConfigurationTest.isolatedUrl.path)?.isolatedVersion
)
}
func test_can_determine_proxy() throws {
let proxied = NginxConfigurationFile.from(filePath: NginxConfigurationTest.proxyUrl.path)!
XCTAssertTrue(proxied.contents.contains("# valet stub: proxy.valet.conf"))
XCTAssertEqual("http://127.0.0.1:90", proxied.proxy)
let normal = NginxConfigurationFile.from(filePath: NginxConfigurationTest.regularUrl.path)!
XCTAssertFalse(normal.contents.contains("# valet stub: proxy.valet.conf"))
XCTAssertEqual(nil, normal.proxy)
}
func test_can_determine_secured_proxy() throws {
let proxied = NginxConfigurationFile.from(filePath: NginxConfigurationTest.secureProxyUrl.path)!
XCTAssertTrue(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
XCTAssertEqual("http://127.0.0.1:90", proxied.proxy)
}
func test_can_determine_proxy_with_custom_tld() throws {
let proxied = NginxConfigurationFile.from(filePath: NginxConfigurationTest.customTldProxyUrl.path)!
XCTAssertTrue(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
XCTAssertEqual("http://localhost:8080", proxied.proxy)
}
}

View File

@@ -1,72 +0,0 @@
//
// ExtensionParserTest.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 13/02/2021.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import XCTest
class PhpExtensionTest: XCTestCase {
static var phpIniFileUrl: URL {
return Bundle(for: Self.self).url(forResource: "php", withExtension: "ini")!
}
func test_can_load_extension() throws {
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
XCTAssertGreaterThan(extensions.count, 0)
}
func test_extension_name_is_correct() throws {
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
let extensionNames = extensions.map { (ext) -> String in
return ext.name
}
// These 6 should be found
XCTAssertTrue(extensionNames.contains("xdebug"))
XCTAssertTrue(extensionNames.contains("imagick"))
XCTAssertTrue(extensionNames.contains("sodium-next"))
XCTAssertTrue(extensionNames.contains("opcache"))
XCTAssertTrue(extensionNames.contains("yaml"))
XCTAssertTrue(extensionNames.contains("custom"))
XCTAssertFalse(extensionNames.contains("fake"))
XCTAssertFalse(extensionNames.contains("nice"))
}
func test_extension_status_is_correct() throws {
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
// xdebug should be enabled
XCTAssertEqual(extensions[0].enabled, true)
// imagick should be disabled
XCTAssertEqual(extensions[1].enabled, false)
}
func test_toggle_works_as_expected() async throws {
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
let extensions = PhpExtension.from(filePath: destination.path)
XCTAssertEqual(extensions.count, 6)
// Try to disable xdebug (should be detected first)!
let xdebug = extensions.first!
XCTAssertTrue(xdebug.name == "xdebug")
XCTAssertEqual(xdebug.enabled, true)
await xdebug.toggle()
XCTAssertEqual(xdebug.enabled, false)
// Check if the file contains the appropriate data
let file = try! String(contentsOf: destination, encoding: .utf8)
XCTAssertTrue(file.contains("; zend_extension=\"xdebug.so\""))
// Make sure if we load the data again, it's disabled
XCTAssertEqual(PhpExtension.from(filePath: destination.path).first!.enabled, false)
}
}

View File

@@ -1,49 +0,0 @@
//
// ValetRcTest.swift
// Unit Tests
//
// Created by Nico Verbruggen on 20/01/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import XCTest
class ValetRcTest: XCTestCase {
// MARK: - Test Files
static var validPath: URL {
return Bundle(for: Self.self)
.url(forResource: "valetrc", withExtension: "valid")!
}
static var brokenPath: URL {
return Bundle(for: Self.self)
.url(forResource: "valetrc", withExtension: "broken")!
}
// MARK: - Tests
func test_can_extract_fields_from_valetrc_file() throws {
let fakeFile = RCFile.fromPath("/Users/fake/file.rc")
XCTAssertNil(fakeFile)
// Can parse the file
let validFile = RCFile.fromPath(ValetRcTest.validPath.path)
XCTAssertNotNil(validFile)
let fields = validFile!.fields
// Correctly parses and trims (and omits double quotes) per line
XCTAssertEqual(fields["PHP"], "php@8.2")
XCTAssertEqual(fields["OTHER"], "thing")
XCTAssertEqual(fields["PHPMON_WATCH"], "true")
XCTAssertEqual(fields["SYNTAX"], "variable")
// Ignores entries prefixed with #
XCTAssertTrue(!fields.keys.contains("#PHP"))
// Ignores invalid lines
XCTAssertTrue(!fields.keys.contains("OOF"))
}
}

View File

@@ -9,13 +9,14 @@
import Testing import Testing
import Foundation import Foundation
@Suite("Api")
struct TestableApiTest { struct TestableApiTest {
@Test func createFakeApi() {
@Test
func createFakeApi() {
let api = TestableApi(responses: [ let api = TestableApi(responses: [
url("https://api.phpmon.test"): FakeApiResponse(statusCode: 200, headers: [:], text: "{\"success\": true}") url("https://api.phpmon.test"): FakeApiResponse(
statusCode: 200,
headers: [:],
text: "{\"success\": true}"
)
]) ])
#expect(api.hasResponse(for: url("https://api.phpmon.test")) == true) #expect(api.hasResponse(for: url("https://api.phpmon.test")) == true)
@@ -25,5 +26,4 @@ struct TestableApiTest {
#expect(response.statusCode == 200) #expect(response.statusCode == 200)
#expect(response.text.contains("success")) #expect(response.text.contains("success"))
} }
} }

View File

@@ -8,11 +8,8 @@
import Testing import Testing
@Suite("Commands")
struct CommandTest { struct CommandTest {
@Test func determinePhpVersion() {
@Test
func determinePhpVersion() {
let version = Command.execute( let version = Command.execute(
path: Paths.php, path: Paths.php,
arguments: ["-v"], arguments: ["-v"],
@@ -24,5 +21,4 @@ struct CommandTest {
#expect(version.contains("built")) #expect(version.contains("built"))
#expect(version.contains("Zend")) #expect(version.contains("Zend"))
} }
} }

View File

@@ -8,7 +8,6 @@
import Testing import Testing
@Suite("Integration")
struct PackagistTest { struct PackagistTest {
@Test func canRetrieveLaravelValetVersion() async { @Test func canRetrieveLaravelValetVersion() async {
let packageToCheck = "laravel/valet" let packageToCheck = "laravel/valet"

View File

@@ -0,0 +1,41 @@
//
// BytePhpPreferenceTest.swift
// Unit Tests
//
// Created by Nico Verbruggen on 04/09/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Testing
struct BytePhpPreferenceTest {
@Test func test_can_extract_memory_value() throws {
let pref = BytePhpPreference(key: "memory_limit")
#expect(pref.internalValue == "512M")
#expect(pref.unit == .megabyte)
#expect(pref.value == 512)
}
@Test func test_can_parse_all_kinds_of_values() throws {
var (unit, value) = BytePhpPreference.readFrom(internalValue: "1G")!
#expect(unit == .gigabyte)
#expect(value == 1)
(unit, value) = BytePhpPreference.readFrom(internalValue: "256M")!
#expect(unit == .megabyte)
#expect(value == 256)
(unit, value) = BytePhpPreference.readFrom(internalValue: "512K")!
#expect(unit == .kilobyte)
#expect(value == 512)
(unit, value) = BytePhpPreference.readFrom(internalValue: "1024")!
#expect(unit == .kilobyte)
#expect(value == 1024)
(unit, value) = BytePhpPreference.readFrom(internalValue: "-1")!
#expect(unit == .kilobyte)
#expect(value == -1)
}
}

View File

@@ -6,41 +6,41 @@
// Copyright © 2023 Nico Verbruggen. All rights reserved. // Copyright © 2023 Nico Verbruggen. All rights reserved.
// //
import XCTest import Testing
import Foundation
class PhpConfigurationFileTest: XCTestCase { class PhpConfigurationFileTest {
static var phpIniFileUrl: URL { static var phpIniFileUrl: URL {
return Bundle(for: Self.self).url(forResource: "php", withExtension: "ini")! return TestBundle.url(forResource: "php", withExtension: "ini")!
} }
func test_can_load_extension() throws { @Test func test_can_load_extension() throws {
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)
#expect(iniFile != nil)
#expect(!iniFile!.extensions.isEmpty)
}
@Test func test_can_check_key_existence() throws {
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)! let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)!
XCTAssertNotNil(iniFile) #expect(iniFile.has(key: "error_reporting"))
#expect(iniFile.has(key: "display_errors"))
XCTAssertGreaterThan(iniFile.extensions.count, 0) #expect(false == iniFile.has(key: "my_unknown_key"))
} }
func test_can_check_key_existence() throws { @Test func test_can_check_key_value() throws {
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)! let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)!
XCTAssertTrue(iniFile.has(key: "error_reporting")) #expect(iniFile.get(for: "error_reporting") != nil)
XCTAssertTrue(iniFile.has(key: "display_errors")) #expect(iniFile.get(for: "error_reporting") == "E_ALL")
XCTAssertFalse(iniFile.has(key: "my_unknown_key"))
#expect(iniFile.get(for: "display_errors") != nil)
#expect(iniFile.get(for: "display_errors") == "On")
} }
func test_can_check_key_value() throws { @Test func test_can_customize_configuration_value() throws {
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)!
XCTAssertNotNil(iniFile.get(for: "error_reporting"))
XCTAssert(iniFile.get(for: "error_reporting") == "E_ALL")
XCTAssertNotNil(iniFile.get(for: "display_errors"))
XCTAssert(iniFile.get(for: "display_errors") == "On")
}
func test_can_customize_configuration_value() throws {
let destination = Utility let destination = Utility
.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")! .copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
@@ -48,15 +48,15 @@ class PhpConfigurationFileTest: XCTestCase {
.from(filePath: destination.path)! .from(filePath: destination.path)!
// 0. Verify the original value // 0. Verify the original value
XCTAssertEqual(configurationFile.get(for: "error_reporting"), "E_ALL") #expect(configurationFile.get(for: "error_reporting") == "E_ALL")
// 1. Change the value // 1. Change the value
try! configurationFile.replace( try! configurationFile.replace(
key: "error_reporting", key: "error_reporting",
value: "E_ALL & ~E_DEPRECATED & ~E_STRICT" value: "E_ALL & ~E_DEPRECATED & ~E_STRICT"
) )
XCTAssertEqual( #expect(
configurationFile.get(for: "error_reporting"), configurationFile.get(for: "error_reporting") ==
"E_ALL & ~E_DEPRECATED & ~E_STRICT" "E_ALL & ~E_DEPRECATED & ~E_STRICT"
) )
@@ -65,20 +65,14 @@ class PhpConfigurationFileTest: XCTestCase {
key: "error_reporting", key: "error_reporting",
value: "error_reporting" value: "error_reporting"
) )
XCTAssertEqual( #expect(configurationFile.get(for: "error_reporting") == "error_reporting")
configurationFile.get(for: "error_reporting"),
"error_reporting"
)
// 3. Verify subsequent saves weren't broken // 3. Verify subsequent saves weren't broken
try! configurationFile.replace( try! configurationFile.replace(
key: "error_reporting", key: "error_reporting",
value: "E_ALL" value: "E_ALL"
) )
XCTAssertEqual( #expect(configurationFile.get(for: "error_reporting") == "E_ALL")
configurationFile.get(for: "error_reporting"),
"E_ALL"
)
} }
} }

View File

@@ -6,53 +6,54 @@
// Copyright © 2023 Nico Verbruggen. All rights reserved. // Copyright © 2023 Nico Verbruggen. All rights reserved.
// //
import XCTest import Testing
import Foundation
class HomebrewPackageTest: XCTestCase { struct HomebrewPackageTest {
// - MARK: SYNTHETIC TESTS // - MARK: SYNTHETIC TESTS
static var jsonBrewFile: URL { static var jsonBrewFile: URL {
return Bundle(for: Self.self) return TestBundle
.url(forResource: "brew-formula", withExtension: "json")! .url(forResource: "brew-formula", withExtension: "json")!
} }
func test_can_load_extension_json() throws { static var jsonBrewServicesFile: URL {
return TestBundle
.url(forResource: "brew-services", withExtension: "json")!
}
@Test func test_can_load_extension_json() throws {
let json = try! String(contentsOf: Self.jsonBrewFile, encoding: .utf8) let json = try! String(contentsOf: Self.jsonBrewFile, encoding: .utf8)
let package = try! JSONDecoder().decode( let package = try! JSONDecoder().decode(
[HomebrewPackage].self, from: json.data(using: .utf8)! [HomebrewPackage].self, from: json.data(using: .utf8)!
).first! ).first!
XCTAssertEqual(package.full_name, "php") #expect(package.full_name == "php")
XCTAssertEqual(package.aliases.first!, "php@8.4") #expect(package.aliases.first! == "php@8.4")
XCTAssertEqual(package.installed.contains(where: { installed in #expect(package.installed.contains(where: { installed in
installed.version.starts(with: "8.4") installed.version.starts(with: "8.4")
}), true) }) == true)
} }
static var jsonBrewServicesFile: URL { @Test func test_can_parse_services_json() throws {
return Bundle(for: Self.self)
.url(forResource: "brew-services", withExtension: "json")!
}
func test_can_parse_services_json() throws {
let json = try! String(contentsOf: Self.jsonBrewServicesFile, encoding: .utf8) let json = try! String(contentsOf: Self.jsonBrewServicesFile, encoding: .utf8)
let services = try! JSONDecoder().decode( let services = try! JSONDecoder().decode(
[HomebrewService].self, from: json.data(using: .utf8)! [HomebrewService].self, from: json.data(using: .utf8)!
) )
XCTAssertGreaterThan(services.count, 0) #expect(!services.isEmpty)
XCTAssertEqual(services.first?.name, "dnsmasq") #expect(services.first?.name == "dnsmasq")
XCTAssertEqual(services.first?.service_name, "homebrew.mxcl.dnsmasq") #expect(services.first?.service_name == "homebrew.mxcl.dnsmasq")
} }
/*
// - MARK: LIVE TESTS // - MARK: LIVE TESTS
/// This test requires that you have a valid Homebrew installation set up, /// This test requires that you have a valid Homebrew installation set up,
/// and requires the Valet services to be installed: php, nginx and dnsmasq. /// and requires the Valet services to be installed: php, nginx and dnsmasq.
/// If this test fails, there is an issue with your Homebrew installation /// If this test fails, there is an issue with your Homebrew installation
/// or the JSON API of the Homebrew output may have changed. /// or the JSON API of the Homebrew output may have changed.
@Test(.disabled("Uses system command; enable at your own risk"))
func test_can_parse_services_json_from_cli_output() async throws { func test_can_parse_services_json_from_cli_output() async throws {
ActiveShell.useSystem() ActiveShell.useSystem()
@@ -65,17 +66,19 @@ class HomebrewPackageTest: XCTestCase {
return ["php", "nginx", "dnsmasq"].contains(service.name) return ["php", "nginx", "dnsmasq"].contains(service.name)
}) })
XCTAssertTrue(services.contains(where: {$0.name == "php"})) #expect(services.contains(where: {$0.name == "php"}))
XCTAssertTrue(services.contains(where: {$0.name == "nginx"})) #expect(services.contains(where: {$0.name == "nginx"}))
XCTAssertTrue(services.contains(where: {$0.name == "dnsmasq"})) #expect(services.contains(where: {$0.name == "dnsmasq"}))
XCTAssertEqual(services.count, 3) #expect(services.count == 3)
} }
/// This test requires that you have a valid Homebrew installation set up, /// This test requires that you have a valid Homebrew installation set up,
/// and requires the `php` formula to be installed. /// and requires the `php` formula to be installed.
/// If this test fails, there is an issue with your Homebrew installation /// If this test fails, there is an issue with your Homebrew installation
/// or the JSON API of the Homebrew output may have changed. /// or the JSON API of the Homebrew output may have changed.
@Test(.disabled("Uses system command; enable at your own risk"))
func test_can_load_extension_json_from_cli_output() async throws { func test_can_load_extension_json_from_cli_output() async throws {
ActiveShell.useSystem() ActiveShell.useSystem()
let package = try! JSONDecoder().decode( let package = try! JSONDecoder().decode(
@@ -83,7 +86,6 @@ class HomebrewPackageTest: XCTestCase {
from: await Shell.pipe("\(Paths.brew) info php --json").out.data(using: .utf8)! from: await Shell.pipe("\(Paths.brew) info php --json").out.data(using: .utf8)!
).first! ).first!
XCTAssertTrue(package.name == "php") #expect(package.full_name == "php")
} }
*/
} }

View File

@@ -0,0 +1,70 @@
//
// NginxConfigurationTest.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 29/11/2021.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Testing
import Foundation
struct NginxConfigurationTest {
// MARK: - Test Files
static var regularUrl: URL {
TestBundle.url(forResource: "nginx-site", withExtension: "test")!
}
static var isolatedUrl: URL {
TestBundle.url(forResource: "nginx-site-isolated", withExtension: "test")!
}
static var proxyUrl: URL {
TestBundle.url(forResource: "nginx-proxy", withExtension: "test")!
}
static var secureProxyUrl: URL {
TestBundle.url(forResource: "nginx-secure-proxy", withExtension: "test")!
}
static var customTldProxyUrl: URL {
TestBundle.url(forResource: "nginx-secure-proxy-custom-tld", withExtension: "test")!
}
// MARK: - Tests
@Test func test_can_determine_site_name_and_tld() throws {
#expect("nginx-site" == NginxConfigurationFile.from(filePath: Self.regularUrl.path)?.domain)
#expect("test" == NginxConfigurationFile.from(filePath: Self.regularUrl.path)?.tld)
}
@Test func test_can_determine_isolation() throws {
#expect(nil == NginxConfigurationFile.from(filePath: Self.regularUrl.path)?.isolatedVersion)
#expect("8.1" == NginxConfigurationFile.from(filePath: Self.isolatedUrl.path)?.isolatedVersion)
}
@Test func test_can_determine_proxy() throws {
let proxied = NginxConfigurationFile.from(filePath: Self.proxyUrl.path)!
#expect(proxied.contents.contains("# valet stub: proxy.valet.conf"))
#expect("http://127.0.0.1:90" == proxied.proxy)
let normal = NginxConfigurationFile.from(filePath: Self.regularUrl.path)!
#expect(false == normal.contents.contains("# valet stub: proxy.valet.conf"))
#expect(nil == normal.proxy)
}
@Test func test_can_determine_secured_proxy() throws {
let proxied = NginxConfigurationFile.from(filePath: Self.secureProxyUrl.path)!
#expect(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
#expect("http://127.0.0.1:90" == proxied.proxy)
}
@Test func test_can_determine_proxy_with_custom_tld() throws {
let proxied = NginxConfigurationFile.from(filePath: Self.customTldProxyUrl.path)!
#expect(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
#expect("http://localhost:8080" == proxied.proxy)
}
}

View File

@@ -0,0 +1,71 @@
//
// ExtensionParserTest.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 13/02/2021.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Testing
import Foundation
struct PhpExtensionTest {
static var phpIniFileUrl: URL {
return TestBundle.url(forResource: "php", withExtension: "ini")!
}
@Test func test_can_load_extension() throws {
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
#expect(!extensions.isEmpty)
}
@Test func test_extension_name_is_correct() throws {
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
let extensionNames = extensions.map { (ext) -> String in
return ext.name
}
// These 6 should be found
#expect(extensionNames.contains("xdebug"))
#expect(extensionNames.contains("imagick"))
#expect(extensionNames.contains("sodium-next"))
#expect(extensionNames.contains("opcache"))
#expect(extensionNames.contains("yaml"))
#expect(extensionNames.contains("custom"))
#expect(extensionNames.contains("fake") == false)
#expect(extensionNames.contains("nice") == false)
}
@Test func test_extension_status_is_correct() throws {
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
// xdebug should be enabled
#expect(extensions[0].enabled == true)
// imagick should be disabled
#expect(extensions[1].enabled == false)
}
@Test func test_toggle_works_as_expected() async throws {
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
let extensions = PhpExtension.from(filePath: destination.path)
#expect(extensions.count == 6)
// Try to disable xdebug (should be detected first)!
let xdebug = extensions.first!
#expect(xdebug.name == "xdebug")
#expect(xdebug.enabled == true)
await xdebug.toggle()
#expect(xdebug.enabled == false)
// Check if the file contains the appropriate data
let file = try! String(contentsOf: destination, encoding: .utf8)
#expect(file.contains("; zend_extension=\"xdebug.so\""))
// Make sure if we load the data again, it's disabled
#expect(PhpExtension.from(filePath: destination.path).first!.enabled == false)
}
}

View File

@@ -9,7 +9,6 @@
import Testing import Testing
import Foundation import Foundation
@Suite("Parsers")
struct ValetConfigurationTest { struct ValetConfigurationTest {
static var jsonConfigFileUrl: URL { static var jsonConfigFileUrl: URL {
return TestBundle.url( return TestBundle.url(
@@ -18,8 +17,7 @@ struct ValetConfigurationTest {
)! )!
} }
@Test("Can load config file") @Test func can_load_config_file() throws {
func can_load_config_file() throws {
let json = try? String( let json = try? String(
contentsOf: Self.jsonConfigFileUrl, contentsOf: Self.jsonConfigFileUrl,
encoding: .utf8 encoding: .utf8
@@ -37,5 +35,4 @@ struct ValetConfigurationTest {
#expect(config.defaultSite == "/Users/username/default-site") #expect(config.defaultSite == "/Users/username/default-site")
#expect(config.loopback == "127.0.0.1") #expect(config.loopback == "127.0.0.1")
} }
} }

View File

@@ -0,0 +1,45 @@
//
// ValetRcTest.swift
// Unit Tests
//
// Created by Nico Verbruggen on 20/01/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Testing
import Foundation
struct ValetRcTest {
// MARK: - Test Files
static var validPath: URL {
return TestBundle.url(forResource: "valetrc", withExtension: "valid")!
}
static var brokenPath: URL {
return TestBundle.url(forResource: "valetrc", withExtension: "broken")!
}
// MARK: - Tests
@Test func test_can_extract_fields_from_valet_rc_file() throws {
let fakeFile = RCFile.fromPath("/Users/fake/file.rc")
#expect(nil == fakeFile)
// Can parse the file
let validFile = RCFile.fromPath(ValetRcTest.validPath.path)
#expect(nil != validFile)
let fields = validFile!.fields
// Correctly parses and trims (and omits double quotes) per line
#expect(fields["PHP"] == "php@8.2")
#expect(fields["OTHER"] == "thing")
#expect(fields["PHPMON_WATCH"] == "true")
#expect(fields["SYNTAX"] == "variable")
// Ignores entries prefixed with #
#expect(!fields.keys.contains("#PHP"))
// Ignores invalid lines
#expect(!fields.keys.contains("OOF"))
}
}