mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2026-04-02 17:40:08 +02:00
♻️ All unit tests pass w/ DI container
This commit is contained in:
@@ -13,6 +13,10 @@
|
||||
031E2B6A2B1525A7007C29E1 /* BrewPhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031E2B682B1525A7007C29E1 /* BrewPhpExtension.swift */; };
|
||||
031E2B6B2B1525A7007C29E1 /* BrewPhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031E2B682B1525A7007C29E1 /* BrewPhpExtension.swift */; };
|
||||
031E2B6C2B1525A7007C29E1 /* BrewPhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031E2B682B1525A7007C29E1 /* BrewPhpExtension.swift */; };
|
||||
031F24802EA1071A00CFB8D9 /* Container+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031F247F2EA1071700CFB8D9 /* Container+Fake.swift */; };
|
||||
031F24812EA1071A00CFB8D9 /* Container+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031F247F2EA1071700CFB8D9 /* Container+Fake.swift */; };
|
||||
031F24822EA1071A00CFB8D9 /* Container+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031F247F2EA1071700CFB8D9 /* Container+Fake.swift */; };
|
||||
031F24832EA1071A00CFB8D9 /* Container+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031F247F2EA1071700CFB8D9 /* Container+Fake.swift */; };
|
||||
03263A382E86D5EC00BD0415 /* UpdateScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03263A372E86D5E800BD0415 /* UpdateScheduler.swift */; };
|
||||
03263A392E86D5EC00BD0415 /* UpdateScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03263A372E86D5E800BD0415 /* UpdateScheduler.swift */; };
|
||||
03263A3A2E86D5EC00BD0415 /* UpdateScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03263A372E86D5E800BD0415 /* UpdateScheduler.swift */; };
|
||||
@@ -975,6 +979,8 @@
|
||||
/* Begin PBXFileReference section */
|
||||
0309E6662B0D4B2F002AC007 /* BrewExtensionsObservable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewExtensionsObservable.swift; sourceTree = "<group>"; };
|
||||
031E2B682B1525A7007C29E1 /* BrewPhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewPhpExtension.swift; sourceTree = "<group>"; };
|
||||
031F247F2EA1071700CFB8D9 /* Container+Fake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Container+Fake.swift"; sourceTree = "<group>"; };
|
||||
031F24842EA1132300CFB8D9 /* PHP Monitor.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "PHP Monitor.xctestplan"; sourceTree = "<group>"; };
|
||||
03263A372E86D5E800BD0415 /* UpdateScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateScheduler.swift; sourceTree = "<group>"; };
|
||||
0329A9A02E92A2A800A62A12 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = "<group>"; };
|
||||
0329A9A22E92A68B00A62A12 /* WarningManager+Evaluations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WarningManager+Evaluations.swift"; sourceTree = "<group>"; };
|
||||
@@ -1609,6 +1615,7 @@
|
||||
C41C1B3522B0097F00E7CF16 /* phpmon */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
031F247F2EA1071700CFB8D9 /* Container+Fake.swift */,
|
||||
0329A9A02E92A2A800A62A12 /* Container.swift */,
|
||||
C4B5853A2770FE2500DA4FBE /* Common */,
|
||||
C41E181722CB61EB0072CF09 /* Domain */,
|
||||
@@ -1967,6 +1974,7 @@
|
||||
C471E79628F9B4260021E251 /* tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
031F24842EA1132300CFB8D9 /* PHP Monitor.xctestplan */,
|
||||
C4E2E86828FC2FF2003B070C /* Shared */,
|
||||
C4F7807A25D7F84B000DBC97 /* unit */,
|
||||
C471E7AE28F9B4940021E251 /* feature */,
|
||||
@@ -2848,6 +2856,7 @@
|
||||
C485707028BF452300539B36 /* PhpDoctorWindowController.swift in Sources */,
|
||||
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
|
||||
C40D725A2A018ACC0054A067 /* BusyStatus.swift in Sources */,
|
||||
031F24802EA1071A00CFB8D9 /* Container+Fake.swift in Sources */,
|
||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */,
|
||||
C4415E8D2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */,
|
||||
@@ -2956,6 +2965,7 @@
|
||||
C471E85A28F9BB650021E251 /* DomainListTypeCell.swift in Sources */,
|
||||
C471E85B28F9BB650021E251 /* DomainListKindCell.swift in Sources */,
|
||||
C4611E5E2AEAD2FB0010BE24 /* ConfigManagerView.swift in Sources */,
|
||||
031F24822EA1071A00CFB8D9 /* Container+Fake.swift in Sources */,
|
||||
C4BF56AD2949381100379603 /* FakeValetInteractor.swift in Sources */,
|
||||
C471E85C28F9BB650021E251 /* DomainListWindowController.swift in Sources */,
|
||||
C471E85D28F9BB650021E251 /* DomainListVC.swift in Sources */,
|
||||
@@ -3255,6 +3265,7 @@
|
||||
C471E7FC28F9BACE0021E251 /* HomebrewDecodable.swift in Sources */,
|
||||
C4BB393C2981AFC700F8E797 /* PhpVersionSource.swift in Sources */,
|
||||
C471E7F628F9BAC80021E251 /* PhpHelper.swift in Sources */,
|
||||
031F24812EA1071A00CFB8D9 /* Container+Fake.swift in Sources */,
|
||||
039E1D7B2E5F0F300072D13D /* ValetUpgrader.swift in Sources */,
|
||||
C471E7EE28F9BAC30021E251 /* Constants.swift in Sources */,
|
||||
C40934A0298EE8E900D25014 /* AppUpdater.swift in Sources */,
|
||||
@@ -3419,6 +3430,7 @@
|
||||
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */,
|
||||
C4159AF728E4D40400545349 /* RealShellTest.swift in Sources */,
|
||||
C450C8C728C919EC002A2B4B /* PreferenceName.swift in Sources */,
|
||||
031F24832EA1071A00CFB8D9 /* Container+Fake.swift in Sources */,
|
||||
C40D725B2A018ACC0054A067 /* BusyStatus.swift in Sources */,
|
||||
032DAC292E8BEB5B0018E01C /* RealApi.swift in Sources */,
|
||||
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */,
|
||||
|
||||
@@ -26,11 +26,16 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<TestPlans>
|
||||
<TestPlanReference
|
||||
reference = "container:tests/PHP Monitor.xctestplan"
|
||||
default = "YES">
|
||||
</TestPlanReference>
|
||||
</TestPlans>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
skipped = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C4F7807825D7F84B000DBC97"
|
||||
@@ -40,7 +45,7 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
skipped = "YES"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
|
||||
@@ -18,30 +18,30 @@ class Actions {
|
||||
// MARK: - Services
|
||||
|
||||
public func linkPhp() async {
|
||||
await brew("link php --overwrite --force")
|
||||
await brew(container, "link php --overwrite --force")
|
||||
}
|
||||
|
||||
public func restartPhpFpm() async {
|
||||
await brew("services restart \(formulae.php)", sudo: formulae.php.elevated)
|
||||
await brew(container, "services restart \(formulae.php)", sudo: formulae.php.elevated)
|
||||
}
|
||||
|
||||
public func restartPhpFpm(version: String) async {
|
||||
let formula = (version == PhpEnvironments.brewPhpAlias) ? "php" : "php@\(version)"
|
||||
await brew("services restart \(formula)", sudo: formulae.php.elevated)
|
||||
await brew(container, "services restart \(formula)", sudo: formulae.php.elevated)
|
||||
}
|
||||
|
||||
public func restartNginx() async {
|
||||
await brew("services restart \(formulae.nginx)", sudo: formulae.nginx.elevated)
|
||||
await brew(container, "services restart \(formulae.nginx)", sudo: formulae.nginx.elevated)
|
||||
}
|
||||
|
||||
public func restartDnsMasq() async {
|
||||
await brew("services restart \(formulae.dnsmasq)", sudo: formulae.dnsmasq.elevated)
|
||||
await brew(container, "services restart \(formulae.dnsmasq)", sudo: formulae.dnsmasq.elevated)
|
||||
}
|
||||
|
||||
public func stopValetServices() async {
|
||||
await brew("services stop \(formulae.php)", sudo: formulae.php.elevated)
|
||||
await brew("services stop \(formulae.nginx)", sudo: formulae.nginx.elevated)
|
||||
await brew("services stop \(formulae.dnsmasq)", sudo: formulae.dnsmasq.elevated)
|
||||
await brew(container, "services stop \(formulae.php)", sudo: formulae.php.elevated)
|
||||
await brew(container, "services stop \(formulae.nginx)", sudo: formulae.nginx.elevated)
|
||||
await brew(container, "services stop \(formulae.dnsmasq)", sudo: formulae.dnsmasq.elevated)
|
||||
}
|
||||
|
||||
public func fixHomebrewPermissions() throws {
|
||||
@@ -134,8 +134,8 @@ class Actions {
|
||||
*/
|
||||
public func fixMyValet() async {
|
||||
await InternalSwitcher().performSwitch(to: PhpEnvironments.brewPhpAlias)
|
||||
await brew("services restart \(formulae.dnsmasq)", sudo: formulae.dnsmasq.elevated)
|
||||
await brew("services restart \(formulae.php)", sudo: formulae.php.elevated)
|
||||
await brew("services restart \(formulae.nginx)", sudo: formulae.nginx.elevated)
|
||||
await brew(container, "services restart \(formulae.dnsmasq)", sudo: formulae.dnsmasq.elevated)
|
||||
await brew(container, "services restart \(formulae.php)", sudo: formulae.php.elevated)
|
||||
await brew(container, "services restart \(formulae.nginx)", sudo: formulae.nginx.elevated)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import Foundation
|
||||
Runs a `brew` command. Can run as superuser.
|
||||
*/
|
||||
func brew(
|
||||
_ container: Container = App.shared.container,
|
||||
_ command: String,
|
||||
sudo: Bool = false,
|
||||
container: Container = App.shared.container,
|
||||
) async {
|
||||
await container.shell.quiet("\(sudo ? "sudo " : "")" + "\(container.paths.brew) \(command)")
|
||||
}
|
||||
@@ -25,12 +25,10 @@ func brew(
|
||||
Runs `sed` in order to replace all occurrences of a string in a specific file with another.
|
||||
*/
|
||||
func sed(
|
||||
_ container: Container = App.shared.container,
|
||||
file: String,
|
||||
original: String,
|
||||
replacement: String,
|
||||
filesystem: FileSystemProtocol = App.shared.container.filesystem,
|
||||
shell: ShellProtocol = App.shared.container.shell,
|
||||
paths: Paths = App.shared.container.paths,
|
||||
replacement: String
|
||||
) async {
|
||||
// Escape slashes (or `sed` won't work)
|
||||
let e_original = original.replacingOccurrences(of: "/", with: "\\/")
|
||||
@@ -38,17 +36,21 @@ func sed(
|
||||
|
||||
// Check if gsed exists; it is able to follow symlinks,
|
||||
// which we want to do to toggle the extension
|
||||
if filesystem.fileExists("\(paths.binPath)/gsed") {
|
||||
await shell.quiet("\(paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
if container.filesystem.fileExists("\(container.paths.binPath)/gsed") {
|
||||
await container.shell.quiet("\(container.paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
} else {
|
||||
await shell.quiet("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
await container.shell.quiet("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Uses `grep` to determine whether a particular query string can be found in a particular file.
|
||||
*/
|
||||
func grepContains(file: String, query: String, shell: ShellProtocol = App.shared.container.shell) async -> Bool {
|
||||
func grepContains(
|
||||
shell: ShellProtocol = App.shared.container.shell,
|
||||
file: String,
|
||||
query: String
|
||||
) async -> Bool {
|
||||
return await shell.pipe("""
|
||||
grep -q '\(query)' \(file); [ $? -eq 0 ] && echo "YES" || echo "NO"
|
||||
""").out
|
||||
|
||||
@@ -10,7 +10,12 @@ import Foundation
|
||||
|
||||
extension String {
|
||||
var replacingTildeWithHomeDirectory: String {
|
||||
return self.replacingOccurrences(of: "~", with: App.shared.container.paths.homePath)
|
||||
// Try and check if there's a shared container
|
||||
if let paths = App.shared.container.paths {
|
||||
return self.replacingOccurrences(of: "~", with: paths.homePath)
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,14 +40,14 @@ class ActivePhpInstallation {
|
||||
|
||||
// MARK: - Initializer
|
||||
|
||||
public static func load() -> ActivePhpInstallation? {
|
||||
let container = App.shared.container
|
||||
|
||||
public static func load(
|
||||
container: Container = App.shared.container
|
||||
) -> ActivePhpInstallation? {
|
||||
if !container.filesystem.fileExists(container.paths.phpConfig) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ActivePhpInstallation()
|
||||
return ActivePhpInstallation(container: container)
|
||||
}
|
||||
|
||||
init(container: Container = App.shared.container) {
|
||||
@@ -83,7 +83,7 @@ class ActivePhpInstallation {
|
||||
|
||||
// See if any extensions are present in said .ini files
|
||||
paths.forEach { (iniFilePath) in
|
||||
if let file = PhpConfigurationFile.from(filePath: iniFilePath) {
|
||||
if let file = PhpConfigurationFile.from(container, filePath: iniFilePath) {
|
||||
iniFiles.append(file)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class PhpEnvironments {
|
||||
*/
|
||||
init(container: Container = App.shared.container) {
|
||||
self.container = container
|
||||
self.currentInstall = ActivePhpInstallation.load()
|
||||
self.currentInstall = ActivePhpInstallation.load(container: container)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +170,7 @@ class PhpEnvironments {
|
||||
|
||||
// Avoid inserting a duplicate
|
||||
if !supportedVersions.contains(phpAlias) && container.filesystem.fileExists("\(container.paths.optPath)/php/bin/php") {
|
||||
let phpAliasInstall = PhpInstallation(phpAlias)
|
||||
let phpAliasInstall = PhpInstallation(container, phpAlias)
|
||||
// Before inserting, ensure that the actual output matches the alias
|
||||
// if that isn't the case, our formula remains out-of-date
|
||||
if !phpAliasInstall.isMissingBinary {
|
||||
@@ -190,7 +190,7 @@ class PhpEnvironments {
|
||||
var mappedVersions: [String: PhpInstallation] = [:]
|
||||
|
||||
availablePhpVersions.forEach { version in
|
||||
mappedVersions[version] = PhpInstallation(version)
|
||||
mappedVersions[version] = PhpInstallation(container, version)
|
||||
}
|
||||
|
||||
cachedPhpInstallations = mappedVersions
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
class PhpConfigurationFile: CreatedFromFile {
|
||||
var container: Container
|
||||
|
||||
struct ConfigValue {
|
||||
let lineIndex: Int
|
||||
@@ -32,24 +33,25 @@ class PhpConfigurationFile: CreatedFromFile {
|
||||
|
||||
/** Resolves a PHP configuration file (.ini) */
|
||||
static func from(
|
||||
filePath: String,
|
||||
container: Container = App.shared.container
|
||||
_ container: Container,
|
||||
filePath: String
|
||||
) -> Self? {
|
||||
let path = filePath.replacingOccurrences(of: "~", with: container.paths.homePath)
|
||||
|
||||
do {
|
||||
let fileContents = try App.shared.container.filesystem.getStringFromFile(path)
|
||||
return Self.init(path: path, contents: fileContents)
|
||||
let fileContents = try container.filesystem.getStringFromFile(path)
|
||||
return Self.init(container, path: path, contents: fileContents)
|
||||
} catch {
|
||||
Log.warn("Could not read the PHP configuration file at: `\(filePath)`")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
required init(path: String, contents: String) {
|
||||
required init(_ container: Container, path: String, contents: String) {
|
||||
self.container = container
|
||||
self.filePath = path
|
||||
self.lines = contents.components(separatedBy: "\n")
|
||||
self.extensions = PhpExtension.from(lines, filePath: path)
|
||||
self.extensions = PhpExtension.from(container, lines, filePath: path)
|
||||
self.content = Self.parseConfig(lines: lines)
|
||||
}
|
||||
|
||||
@@ -116,7 +118,7 @@ class PhpConfigurationFile: CreatedFromFile {
|
||||
public func reload() {
|
||||
self.lines = try! String(contentsOfFile: self.filePath)
|
||||
.components(separatedBy: "\n")
|
||||
self.extensions = PhpExtension.from(lines, filePath: self.filePath)
|
||||
self.extensions = PhpExtension.from(container, lines, filePath: self.filePath)
|
||||
self.content = Self.parseConfig(lines: lines)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
/**
|
||||
A PHP extension that was detected in the php.ini file.
|
||||
@@ -15,8 +16,8 @@ import Foundation
|
||||
- Note: You need to know more about regular expressions to be able to deal with these NSRegularExpression
|
||||
instances. You can find more information here: https://nshipster.com/swift-regular-expressions/
|
||||
*/
|
||||
@ContainerAccess
|
||||
class PhpExtension {
|
||||
|
||||
/// The file where this extension was located.
|
||||
var file: String
|
||||
|
||||
@@ -54,7 +55,9 @@ class PhpExtension {
|
||||
/**
|
||||
When registering an extension, we do that based on the line found inside the .ini file.
|
||||
*/
|
||||
init(_ line: String, file: String) {
|
||||
init(_ container: Container, _ line: String, file: String) {
|
||||
self.container = container
|
||||
|
||||
let regex = try! NSRegularExpression(pattern: Self.extensionRegex, options: [])
|
||||
let match = regex.matches(in: line, options: [], range: NSRange(location: 0, length: line.count)).first
|
||||
let range = Range(match!.range(withName: "name"), in: line)!
|
||||
@@ -82,7 +85,7 @@ class PhpExtension {
|
||||
// ENABLED: Line where the comment delimiter (;) is removed
|
||||
: line.replacingOccurrences(of: "; ", with: "")
|
||||
|
||||
await sed(file: file, original: line, replacement: newLine)
|
||||
await sed(container, file: file, original: line, replacement: newLine)
|
||||
|
||||
self.enabled = !newLine.starts(with: ";")
|
||||
self.line = newLine
|
||||
@@ -96,15 +99,15 @@ class PhpExtension {
|
||||
|
||||
// MARK: - Static Methods
|
||||
|
||||
static func from(_ lines: [String], filePath: String) -> [PhpExtension] {
|
||||
static func from(_ container: Container, _ lines: [String], filePath: String) -> [PhpExtension] {
|
||||
return lines.filter {
|
||||
return $0.range(of: Self.extensionRegex, options: .regularExpression) != nil
|
||||
}.map {
|
||||
return PhpExtension($0, file: filePath)
|
||||
return PhpExtension(container, $0, file: filePath)
|
||||
}
|
||||
}
|
||||
|
||||
static func from(filePath: String) -> [PhpExtension] {
|
||||
static func from(_ container: Container, filePath: String) -> [PhpExtension] {
|
||||
let file = try? String(contentsOfFile: filePath)
|
||||
|
||||
if file == nil {
|
||||
@@ -113,6 +116,7 @@ class PhpExtension {
|
||||
}
|
||||
|
||||
return Self.from(
|
||||
container,
|
||||
file!.components(separatedBy: "\n"),
|
||||
filePath: filePath
|
||||
)
|
||||
|
||||
@@ -11,7 +11,6 @@ import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class PhpInstallation {
|
||||
|
||||
var versionNumber: VersionNumber
|
||||
|
||||
var iniFiles: [PhpConfigurationFile] = []
|
||||
@@ -40,7 +39,7 @@ class PhpInstallation {
|
||||
In order to determine details about a PHP installation,
|
||||
we’ll simply run `php-config --version` in the relevant directory.
|
||||
*/
|
||||
init(container: Container = App.shared.container, _ version: String) {
|
||||
init(_ container: Container, _ version: String) {
|
||||
self.container = container
|
||||
|
||||
let phpConfigExecutablePath = "\(container.paths.optPath)/php@\(version)/bin/php-config",
|
||||
@@ -105,7 +104,7 @@ class PhpInstallation {
|
||||
|
||||
// See if any extensions are present in said .ini files
|
||||
paths.forEach { (iniFilePath) in
|
||||
if let file = PhpConfigurationFile.from(filePath: iniFilePath) {
|
||||
if let file = PhpConfigurationFile.from(container, filePath: iniFilePath) {
|
||||
iniFiles.append(file)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class InternalSwitcher: PhpSwitcher {
|
||||
|
||||
if Valet.installed {
|
||||
Log.info("Restarting nginx, just to be sure!")
|
||||
await brew("services restart nginx", sudo: true)
|
||||
await brew(container, "services restart nginx", sudo: true)
|
||||
}
|
||||
|
||||
Log.info("The new version(s) have been linked!")
|
||||
@@ -78,10 +78,10 @@ class InternalSwitcher: PhpSwitcher {
|
||||
|
||||
func unlinkAndStopPhpVersion(_ version: String) async {
|
||||
let formula = (version == PhpEnvironments.brewPhpAlias) ? "php" : "php@\(version)"
|
||||
await brew("unlink \(formula)")
|
||||
await brew(container, "unlink \(formula)")
|
||||
|
||||
if Valet.installed {
|
||||
await brew("services stop \(formula)", sudo: true)
|
||||
await brew(container, "services stop \(formula)", sudo: true)
|
||||
Log.info("Unlinked and stopped services for \(formula)")
|
||||
} else {
|
||||
Log.info("Unlinked \(formula)")
|
||||
@@ -93,13 +93,13 @@ class InternalSwitcher: PhpSwitcher {
|
||||
|
||||
if primary {
|
||||
Log.info("\(formula) is the primary formula, linking...")
|
||||
await brew("link \(formula) --overwrite --force")
|
||||
await brew(container, "link \(formula) --overwrite --force")
|
||||
} else {
|
||||
Log.info("\(formula) is an isolated PHP version, not linking!")
|
||||
}
|
||||
|
||||
if Valet.installed {
|
||||
await brew("services start \(formula)", sudo: true)
|
||||
await brew(container, "services start \(formula)", sudo: true)
|
||||
|
||||
if Valet.enabled(feature: .isolatedSites) && primary {
|
||||
let socketVersion = version.replacingOccurrences(of: ".", with: "")
|
||||
|
||||
@@ -9,7 +9,5 @@
|
||||
import Foundation
|
||||
|
||||
protocol CreatedFromFile {
|
||||
|
||||
static func from(filePath: String, container: Container) -> Self?
|
||||
|
||||
static func from(_ container: Container, filePath: String) -> Self?
|
||||
}
|
||||
|
||||
34
phpmon/Container+Fake.swift
Normal file
34
phpmon/Container+Fake.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Container+Fake.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 16/10/2025.
|
||||
// Copyright © 2025 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
extension Container {
|
||||
public func overrideFake(
|
||||
shellExpectations: [String: BatchFakeShellOutput] = [:],
|
||||
fileSystemFiles: [String: FakeFile] = [:],
|
||||
commands: [String: String] = [:]
|
||||
) {
|
||||
self.shell = TestableShell(expectations: shellExpectations)
|
||||
self.filesystem = TestableFileSystem(files: fileSystemFiles)
|
||||
self.command = TestableCommand(commands: commands)
|
||||
}
|
||||
|
||||
public static func fake(
|
||||
shell: [String: BatchFakeShellOutput] = [:],
|
||||
files: [String: FakeFile] = [:],
|
||||
commands: [String: String] = [:]
|
||||
) -> Container {
|
||||
let container = Container()
|
||||
container.prepare()
|
||||
container.overrideFake(
|
||||
shellExpectations: shell,
|
||||
fileSystemFiles: files,
|
||||
commands: commands
|
||||
)
|
||||
return container
|
||||
}
|
||||
}
|
||||
@@ -37,9 +37,9 @@ class Container {
|
||||
self.command = TestableCommand(commands: config.commandOutput)
|
||||
}
|
||||
|
||||
public func overrideFake() {
|
||||
self.shell = TestableShell(expectations: [:])
|
||||
self.filesystem = TestableFileSystem(files: [:])
|
||||
self.command = TestableCommand(commands: [:])
|
||||
public static func real() -> Container {
|
||||
let container = Container()
|
||||
container.prepare()
|
||||
return container
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ class App {
|
||||
The dependency container.
|
||||
This is supposed to be injected, so direct access is discouraged.
|
||||
*/
|
||||
let container = Container()
|
||||
var container: Container = Container()
|
||||
|
||||
/** The list of preferences that are currently active. */
|
||||
var preferences: [PreferenceName: Bool]!
|
||||
|
||||
@@ -28,7 +28,7 @@ class AppUpdater {
|
||||
|
||||
let caskUrl = Constants.Urls.UpdateCheckEndpoint
|
||||
|
||||
guard let caskFile = await CaskFile.from(url: caskUrl) else {
|
||||
guard let caskFile = await CaskFile.from(App.shared.container, url: caskUrl) else {
|
||||
Log.err("The contents of the CaskFile at '\(caskUrl.absoluteString)' could not be retrieved.")
|
||||
presentCouldNotRetrieveUpdateIfInteractive()
|
||||
return .networkError
|
||||
|
||||
@@ -110,6 +110,7 @@ class ValetServicesManager: ServicesManager {
|
||||
|
||||
// Run the command
|
||||
await brew(
|
||||
container,
|
||||
"services \(action) \(wrapper.formula.name)",
|
||||
sudo: wrapper.formula.elevated
|
||||
)
|
||||
|
||||
@@ -30,16 +30,18 @@ struct BrewPhpExtension: Hashable, Comparable {
|
||||
return "\(name)@\(phpVersion)"
|
||||
}
|
||||
|
||||
init(path: String, name: String, phpVersion: String) {
|
||||
init(_ container: Container, path: String, name: String, phpVersion: String) {
|
||||
self.path = path
|
||||
self.name = name
|
||||
self.phpVersion = phpVersion
|
||||
|
||||
self.isInstalled = BrewPhpExtension.hasInstallationReceipt(
|
||||
for: "\(name)@\(phpVersion)"
|
||||
container, for: "\(name)@\(phpVersion)"
|
||||
)
|
||||
|
||||
self.dependencies = BrewPhpExtension.extractDependencies(from: path)
|
||||
self.dependencies = BrewPhpExtension.extractDependencies(
|
||||
container, from: path
|
||||
)
|
||||
}
|
||||
|
||||
var hasAlternativeInstall: Bool {
|
||||
@@ -58,8 +60,8 @@ struct BrewPhpExtension: Hashable, Comparable {
|
||||
.first { $0.dependencies.contains("shivammathur/extensions/\(self.formulaName)") }
|
||||
}
|
||||
|
||||
static func hasInstallationReceipt(for formulaName: String) -> Bool {
|
||||
return App.shared.container.filesystem.fileExists("\(App.shared.container.paths.optPath)/\(formulaName)/INSTALL_RECEIPT.json")
|
||||
static func hasInstallationReceipt(_ container: Container, for formulaName: String) -> Bool {
|
||||
return container.filesystem.fileExists("\(container.paths.optPath)/\(formulaName)/INSTALL_RECEIPT.json")
|
||||
}
|
||||
|
||||
static func < (lhs: BrewPhpExtension, rhs: BrewPhpExtension) -> Bool {
|
||||
@@ -70,11 +72,11 @@ struct BrewPhpExtension: Hashable, Comparable {
|
||||
return lhs.name == rhs.name
|
||||
}
|
||||
|
||||
private static func extractDependencies(from path: String) -> [String] {
|
||||
private static func extractDependencies(_ container: Container, from path: String) -> [String] {
|
||||
let regexPattern = #"depends_on "(.*)""#
|
||||
var dependencies: [String] = []
|
||||
|
||||
guard let content = try? App.shared.container.filesystem.getStringFromFile(path) else {
|
||||
guard let content = try? container.filesystem.getStringFromFile(path) else {
|
||||
return []
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,8 @@ import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
struct BrewPhpFormula: Equatable {
|
||||
var container: Container {
|
||||
return App.shared.container
|
||||
}
|
||||
/// The dependency container.
|
||||
let container: Container
|
||||
|
||||
/// Name of the formula.
|
||||
let name: String
|
||||
@@ -38,12 +37,14 @@ struct BrewPhpFormula: Equatable {
|
||||
}
|
||||
|
||||
init(
|
||||
_ container: Container,
|
||||
name: String,
|
||||
displayName: String,
|
||||
installedVersion: String?,
|
||||
upgradeVersion: String?,
|
||||
prerelease: Bool = false
|
||||
) {
|
||||
self.container = container
|
||||
self.name = name
|
||||
self.displayName = displayName
|
||||
self.installedVersion = installedVersion
|
||||
|
||||
@@ -63,6 +63,7 @@ class BrewPhpFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
}
|
||||
|
||||
return BrewPhpFormula(
|
||||
container,
|
||||
name: formula,
|
||||
displayName: "PHP \(version)",
|
||||
installedVersion: fullVersion,
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
import Foundation
|
||||
|
||||
class BrewTapFormulae {
|
||||
public static func from(tap: String) -> [String: [BrewPhpExtension]] {
|
||||
let directory = "\(App.shared.container.paths.tapPath)/\(tap)/Formula"
|
||||
public static func from(_ container: Container, tap: String) -> [String: [BrewPhpExtension]] {
|
||||
let directory = "\(container.paths.tapPath)/\(tap)/Formula"
|
||||
|
||||
let files = try? App.shared.container.filesystem.getShallowContentsOfDirectory(directory)
|
||||
let files = try? container.filesystem.getShallowContentsOfDirectory(directory)
|
||||
|
||||
var availableExtensions = [String: [BrewPhpExtension]]()
|
||||
|
||||
@@ -35,7 +35,8 @@ class BrewTapFormulae {
|
||||
|
||||
// Create a new BrewPhpExtension object (determines if installed)
|
||||
let phpExtension = BrewPhpExtension(
|
||||
path: "\(App.shared.container.paths.tapPath)/\(tap)/Formula/\(file)",
|
||||
container,
|
||||
path: "\(container.paths.tapPath)/\(tap)/Formula/\(file)",
|
||||
name: phpExtensionName,
|
||||
phpVersion: phpVersion
|
||||
)
|
||||
|
||||
@@ -24,11 +24,11 @@ struct CaskFile {
|
||||
return self.properties["version"]!
|
||||
}
|
||||
|
||||
private static func loadFromApi(_ url: URL) async -> String {
|
||||
if App.hasLoadedTestableConfiguration || url.absoluteString.contains("https://raw.githubusercontent.com") {
|
||||
return await App.shared.container.shell.pipe("curl -s --max-time 10 '\(url.absoluteString)'").out
|
||||
private static func loadFromApi(_ container: Container, _ url: URL) async -> String {
|
||||
if isRunningTests || App.hasLoadedTestableConfiguration || url.absoluteString.contains("https://raw.githubusercontent.com") {
|
||||
return await container.shell.pipe("curl -s --max-time 10 '\(url.absoluteString)'").out
|
||||
} else {
|
||||
return await App.shared.container.shell.pipe("""
|
||||
return await container.shell.pipe("""
|
||||
curl -s --max-time 10 \
|
||||
-H "User-Agent: phpmon-curl/1.0" \
|
||||
-H "X-phpmon-version: \(App.shortVersion) (\(App.bundleVersion))" \
|
||||
@@ -39,13 +39,13 @@ struct CaskFile {
|
||||
}
|
||||
}
|
||||
|
||||
public static func from(url: URL) async -> CaskFile? {
|
||||
public static func from(_ container: Container, url: URL) async -> CaskFile? {
|
||||
var string: String?
|
||||
|
||||
if url.scheme == "file" {
|
||||
string = try? String(contentsOf: url)
|
||||
} else {
|
||||
string = await CaskFile.loadFromApi(url)
|
||||
string = await CaskFile.loadFromApi(container, url)
|
||||
}
|
||||
|
||||
guard let string else {
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
import Foundation
|
||||
|
||||
class NginxConfigurationFile: CreatedFromFile {
|
||||
|
||||
/// Contents of the Nginx file in question, as a string.
|
||||
var contents: String!
|
||||
|
||||
@@ -21,8 +20,8 @@ class NginxConfigurationFile: CreatedFromFile {
|
||||
|
||||
/** Resolves an nginx configuration file (.conf) */
|
||||
static func from(
|
||||
_ container: Container,
|
||||
filePath: String,
|
||||
container: Container = App.shared.container
|
||||
) -> Self? {
|
||||
let path = filePath.replacingOccurrences(of: "~", with: container.paths.homePath)
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ class ValetDomainScanner: DomainScanner {
|
||||
private func isSite(_ entry: String, forPath path: String) -> Bool {
|
||||
let siteDir = path + "/" + entry
|
||||
|
||||
return (App.shared.container.filesystem.isDirectory(siteDir) || App.shared.container.filesystem.isSymlink(siteDir))
|
||||
return (container.filesystem.isDirectory(siteDir) || container.filesystem.isSymlink(siteDir))
|
||||
}
|
||||
|
||||
// MARK: - Proxies
|
||||
@@ -100,7 +100,7 @@ class ValetDomainScanner: DomainScanner {
|
||||
return !$0.starts(with: ".")
|
||||
}
|
||||
.compactMap {
|
||||
return NginxConfigurationFile.from(filePath: "\(directoryPath)/\($0)")
|
||||
return NginxConfigurationFile.from(container, filePath: "\(directoryPath)/\($0)")
|
||||
}
|
||||
.filter {
|
||||
return $0.proxy != nil
|
||||
|
||||
@@ -40,7 +40,7 @@ class FakeValetSite: ValetSite {
|
||||
}
|
||||
|
||||
if let isolated = isolated {
|
||||
self.isolatedPhpVersion = PhpInstallation(isolated)
|
||||
self.isolatedPhpVersion = PhpInstallation(container, isolated)
|
||||
}
|
||||
|
||||
if container.phpEnvs.currentInstall != nil {
|
||||
|
||||
@@ -280,7 +280,7 @@ class ValetSite: ValetListable {
|
||||
) -> String? {
|
||||
if container.filesystem.fileExists(filePath) {
|
||||
return NginxConfigurationFile
|
||||
.from(filePath: filePath)?
|
||||
.from(container, filePath: filePath)?
|
||||
.isolatedVersion ?? nil
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ class BytePhpPreference: PhpPreference {
|
||||
didSet { updatedFieldValue() }
|
||||
}
|
||||
|
||||
override init(container: Container = App.shared.container, key: String) {
|
||||
override init(_ container: Container = App.shared.container, key: String) {
|
||||
let value = container.command.execute(
|
||||
path: container.paths.php, arguments: ["-r", "echo ini_get('\(key)');"],
|
||||
trimNewlines: false
|
||||
@@ -48,7 +48,7 @@ class BytePhpPreference: PhpPreference {
|
||||
self.value = value
|
||||
}
|
||||
|
||||
super.init(container: container, key: key)
|
||||
super.init(container, key: key)
|
||||
}
|
||||
|
||||
// MARK: Save Value
|
||||
|
||||
@@ -14,7 +14,7 @@ import ContainerMacro
|
||||
class PhpPreference {
|
||||
let key: String
|
||||
|
||||
init(container: Container = App.shared.container, key: String) {
|
||||
init(_ container: Container = App.shared.container, key: String) {
|
||||
self.container = container
|
||||
self.key = key
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ extension WarningManager {
|
||||
return [
|
||||
Warning(
|
||||
command: {
|
||||
return await App.shared.container.shell.pipe("sysctl -n sysctl.proc_translated").out
|
||||
return await self.container.shell.pipe("sysctl -n sysctl.proc_translated").out
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines) == "1"
|
||||
},
|
||||
name: "Running PHP Monitor with Rosetta on Apple Silicon",
|
||||
@@ -23,8 +23,8 @@ extension WarningManager {
|
||||
),
|
||||
Warning(
|
||||
command: {
|
||||
return !App.shared.container.shell.PATH.contains("\(App.shared.container.paths.homePath)/.config/phpmon/bin") &&
|
||||
!App.shared.container.filesystem.isWriteableFile("/usr/local/bin/")
|
||||
return !self.container.shell.PATH.contains("\(self.container.paths.homePath)/.config/phpmon/bin") &&
|
||||
!self.container.filesystem.isWriteableFile("/usr/local/bin/")
|
||||
},
|
||||
name: "Helpers cannot be symlinked and not in PATH",
|
||||
title: "warnings.helper_permissions.title",
|
||||
@@ -34,7 +34,7 @@ extension WarningManager {
|
||||
"warnings.helper_permissions.symlink"
|
||||
] },
|
||||
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries",
|
||||
fix: App.shared.container.paths.shell == "/bin/zsh" ? {
|
||||
fix: self.container.paths.shell == "/bin/zsh" ? {
|
||||
// Add to PATH
|
||||
await ZshRunCommand().addPhpMonitorPath()
|
||||
// Finally, perform environment checks again
|
||||
@@ -43,7 +43,7 @@ extension WarningManager {
|
||||
),
|
||||
Warning(
|
||||
command: {
|
||||
App.shared.container.phpEnvs.currentInstall?.extensions.contains { $0.name == "xdebug" } ?? false
|
||||
self.container.phpEnvs.currentInstall?.extensions.contains { $0.name == "xdebug" } ?? false
|
||||
&& !Xdebug().enabled
|
||||
},
|
||||
name: "Missing configuration file for `xdebug.mode`",
|
||||
@@ -53,17 +53,17 @@ extension WarningManager {
|
||||
] },
|
||||
url: "https://xdebug.org/docs/install#mode",
|
||||
fix: {
|
||||
if let php = App.shared.container.phpEnvs.currentInstall {
|
||||
if let php = self.container.phpEnvs.currentInstall {
|
||||
if let xdebug = php.extensions.first(where: { $0.name == "xdebug" }),
|
||||
let original = try? App.shared.container.filesystem.getStringFromFile(xdebug.file) {
|
||||
let original = try? self.container.filesystem.getStringFromFile(xdebug.file) {
|
||||
// Append xdebug.mode = off to the file
|
||||
try? App.shared.container.filesystem.writeAtomicallyToFile(
|
||||
try? self.container.filesystem.writeAtomicallyToFile(
|
||||
xdebug.file,
|
||||
content: original + "\nxdebug.mode = off"
|
||||
)
|
||||
|
||||
// Reload extension configuration by updating PHP installation info (reload)
|
||||
App.shared.container.phpEnvs.currentInstall = ActivePhpInstallation()
|
||||
self.container.phpEnvs.currentInstall = ActivePhpInstallation()
|
||||
|
||||
// Finally, reload warnings
|
||||
await self.checkEnvironment()
|
||||
@@ -82,7 +82,7 @@ extension WarningManager {
|
||||
] },
|
||||
url: "https://github.com/shivammathur/homebrew-php",
|
||||
fix: {
|
||||
await App.shared.container.shell.quiet("brew tap shivammathur/php")
|
||||
await self.container.shell.quiet("brew tap shivammathur/php")
|
||||
await BrewDiagnostics.shared.loadInstalledTaps()
|
||||
await self.checkEnvironment()
|
||||
}
|
||||
@@ -98,7 +98,7 @@ extension WarningManager {
|
||||
] },
|
||||
url: "https://github.com/shivammathur/homebrew-extensions",
|
||||
fix: {
|
||||
await App.shared.container.shell.quiet("brew tap shivammathur/extensions")
|
||||
await self.container.shell.quiet("brew tap shivammathur/extensions")
|
||||
await BrewDiagnostics.shared.loadInstalledTaps()
|
||||
await self.checkEnvironment()
|
||||
}
|
||||
|
||||
@@ -23,7 +23,10 @@ class BrewExtensionsObservable: ObservableObject {
|
||||
}
|
||||
|
||||
public func loadExtensionData(for version: String) {
|
||||
let tapFormulae = BrewTapFormulae.from(tap: "shivammathur/homebrew-extensions")
|
||||
let tapFormulae = BrewTapFormulae.from(
|
||||
App.shared.container,
|
||||
tap: "shivammathur/homebrew-extensions"
|
||||
)
|
||||
|
||||
if let filteredTapFormulae = tapFormulae[version] {
|
||||
self.extensions = filteredTapFormulae
|
||||
|
||||
@@ -11,8 +11,11 @@ import Foundation
|
||||
// swiftlint:disable function_body_length
|
||||
class FakeBrewFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
public func loadPhpVersions(loadOutdated: Bool) async -> [BrewPhpFormula] {
|
||||
let container = App.shared.container
|
||||
|
||||
return [
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php@9.9",
|
||||
displayName: "PHP 9.9",
|
||||
installedVersion: nil,
|
||||
@@ -20,6 +23,7 @@ class FakeBrewFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
prerelease: true
|
||||
),
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php@8.4",
|
||||
displayName: "PHP 8.4",
|
||||
installedVersion: nil,
|
||||
@@ -27,6 +31,7 @@ class FakeBrewFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
prerelease: true
|
||||
),
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php",
|
||||
displayName: "PHP 8.3",
|
||||
installedVersion: nil,
|
||||
@@ -34,42 +39,49 @@ class FakeBrewFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
prerelease: true
|
||||
),
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php@8.2",
|
||||
displayName: "PHP 8.2",
|
||||
installedVersion: "8.2.3",
|
||||
upgradeVersion: "8.2.4"
|
||||
),
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php@8.1",
|
||||
displayName: "PHP 8.1",
|
||||
installedVersion: "8.1.17",
|
||||
upgradeVersion: nil
|
||||
),
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php@8.0",
|
||||
displayName: "PHP 8.0",
|
||||
installedVersion: nil,
|
||||
upgradeVersion: nil
|
||||
),
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php@7.4",
|
||||
displayName: "PHP 7.4",
|
||||
installedVersion: nil,
|
||||
upgradeVersion: nil
|
||||
),
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php@7.3",
|
||||
displayName: "PHP 7.3",
|
||||
installedVersion: nil,
|
||||
upgradeVersion: nil
|
||||
),
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php@7.2",
|
||||
displayName: "PHP 7.2",
|
||||
installedVersion: nil,
|
||||
upgradeVersion: nil
|
||||
),
|
||||
BrewPhpFormula(
|
||||
container,
|
||||
name: "php@7.1",
|
||||
displayName: "PHP 7.1",
|
||||
installedVersion: nil,
|
||||
|
||||
25
tests/PHP Monitor.xctestplan
Normal file
25
tests/PHP Monitor.xctestplan
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"configurations" : [
|
||||
{
|
||||
"id" : "CDAF5C24-6A14-4AD7-B204-61734226DBF0",
|
||||
"name" : "Configuration 1",
|
||||
"options" : {
|
||||
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultOptions" : {
|
||||
|
||||
},
|
||||
"testTargets" : [
|
||||
{
|
||||
"parallelizable" : false,
|
||||
"target" : {
|
||||
"containerPath" : "container:PHP Monitor.xcodeproj",
|
||||
"identifier" : "C4F7807825D7F84B000DBC97",
|
||||
"name" : "Unit Tests"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 1
|
||||
}
|
||||
@@ -9,13 +9,11 @@
|
||||
import XCTest
|
||||
|
||||
class FeatureTestCase: XCTestCase {
|
||||
// TODO: make fake filesystem accessible via test case
|
||||
|
||||
public func assertFileSystemHas(
|
||||
_ path: String,
|
||||
file: StaticString = #filePath,
|
||||
line: UInt = #line,
|
||||
fs: TestableFileSystem
|
||||
in fs: TestableFileSystem
|
||||
) {
|
||||
XCTAssertTrue(fs.files.keys.contains(path), file: file, line: line)
|
||||
}
|
||||
@@ -24,7 +22,7 @@ class FeatureTestCase: XCTestCase {
|
||||
_ path: String,
|
||||
file: StaticString = #filePath,
|
||||
line: UInt = #line,
|
||||
fs: TestableFileSystem
|
||||
in fs: TestableFileSystem
|
||||
) {
|
||||
XCTAssertFalse(fs.files.keys.contains(path), file: file, line: line)
|
||||
}
|
||||
@@ -34,7 +32,7 @@ class FeatureTestCase: XCTestCase {
|
||||
contents: String,
|
||||
file: StaticString = #filePath,
|
||||
line: UInt = #line,
|
||||
fs: TestableFileSystem
|
||||
in fs: TestableFileSystem
|
||||
) {
|
||||
XCTAssertEqual(contents, fs.files[path]?.content, file: file, line: line)
|
||||
}
|
||||
|
||||
@@ -9,39 +9,36 @@
|
||||
import XCTest
|
||||
|
||||
final class InternalSwitcherTest: FeatureTestCase {
|
||||
|
||||
public func testDefaultPhpFpmPoolIsMoved() async {
|
||||
ActiveFileSystem.useTestable([
|
||||
let c = Container.fake(files: [
|
||||
"/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf": .fake(.text)
|
||||
])
|
||||
]), fs = c.filesystem as! TestableFileSystem
|
||||
|
||||
let outcome = await InternalSwitcher().disableDefaultPhpFpmPool("8.1")
|
||||
let outcome = await InternalSwitcher(container: c).disableDefaultPhpFpmPool("8.1")
|
||||
XCTAssertTrue(outcome)
|
||||
|
||||
assertFileSystemHas("/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf.disabled-by-phpmon")
|
||||
assertFileSystemDoesNotHave("/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf")
|
||||
assertFileSystemHas("/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf.disabled-by-phpmon", in: fs)
|
||||
assertFileSystemDoesNotHave("/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf", in: fs)
|
||||
}
|
||||
|
||||
public func testExistingDisabledByPhpMonFileIsRemoved() async {
|
||||
ActiveFileSystem.useTestable([
|
||||
let c = Container.fake(files: [
|
||||
"/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf": .fake(.text, "system generated"),
|
||||
"/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf.disabled-by-phpmon": .fake(.text, "phpmon generated")
|
||||
])
|
||||
]), fs = c.filesystem as! TestableFileSystem
|
||||
|
||||
assertFileHasContents(
|
||||
"/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf.disabled-by-phpmon",
|
||||
contents: "phpmon generated"
|
||||
)
|
||||
assertFileHasContents("/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf.disabled-by-phpmon",
|
||||
contents: "phpmon generated", in: fs)
|
||||
|
||||
let outcome = await InternalSwitcher().disableDefaultPhpFpmPool("8.1")
|
||||
let outcome = await InternalSwitcher(container: c).disableDefaultPhpFpmPool("8.1")
|
||||
XCTAssertTrue(outcome)
|
||||
|
||||
assertFileSystemHas("/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf.disabled-by-phpmon")
|
||||
assertFileSystemDoesNotHave("/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf")
|
||||
assertFileSystemHas("/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf.disabled-by-phpmon", in: fs)
|
||||
assertFileSystemDoesNotHave("/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf", in: fs)
|
||||
|
||||
assertFileHasContents(
|
||||
"/opt/homebrew/etc/php/8.1/php-fpm.d/www.conf.disabled-by-phpmon",
|
||||
contents: "system generated"
|
||||
contents: "system generated", in: fs
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@ import Testing
|
||||
|
||||
struct CommandTest {
|
||||
@Test func determinePhpVersion() {
|
||||
let version = Command.execute(
|
||||
path: Paths.php,
|
||||
let container = Container.real()
|
||||
|
||||
let version = container.command.execute(
|
||||
path: container.paths.php,
|
||||
arguments: ["-v"],
|
||||
trimNewlines: false
|
||||
)
|
||||
|
||||
@@ -9,8 +9,14 @@
|
||||
import Testing
|
||||
|
||||
struct BytePhpPreferenceTest {
|
||||
var container: Container
|
||||
|
||||
init () async throws {
|
||||
container = Container.real()
|
||||
}
|
||||
|
||||
@Test func can_extract_memory_value() throws {
|
||||
let pref = BytePhpPreference(key: "memory_limit")
|
||||
let pref = BytePhpPreference(container, key: "memory_limit")
|
||||
|
||||
#expect(pref.internalValue == "512M")
|
||||
#expect(pref.unit == .megabyte)
|
||||
|
||||
@@ -11,9 +11,14 @@ import Foundation
|
||||
|
||||
@Suite(.serialized)
|
||||
struct CaskFileParserTest {
|
||||
var container: Container
|
||||
|
||||
init() async throws {
|
||||
ActiveShell.useSystem()
|
||||
container = Container.real()
|
||||
}
|
||||
|
||||
var Shell: ShellProtocol {
|
||||
return container.shell
|
||||
}
|
||||
|
||||
// MARK: - Test Files
|
||||
@@ -22,7 +27,7 @@ struct CaskFileParserTest {
|
||||
}
|
||||
|
||||
@Test func can_extract_fields_from_cask_file() async throws {
|
||||
guard let caskFile = await CaskFile.from(url: CaskFileParserTest.exampleFilePath) else {
|
||||
guard let caskFile = await CaskFile.from(container, url: CaskFileParserTest.exampleFilePath) else {
|
||||
Issue.record("The CaskFile could not be parsed, check the log for more info")
|
||||
return
|
||||
}
|
||||
@@ -48,7 +53,7 @@ struct CaskFileParserTest {
|
||||
@Test func can_extract_fields_from_remote_cask_file() async throws {
|
||||
let url = URL(string: "https://raw.githubusercontent.com/nicoverbruggen/homebrew-cask/master/Casks/phpmon.rb")!
|
||||
|
||||
guard let caskFile = await CaskFile.from(url: url) else {
|
||||
guard let caskFile = await CaskFile.from(container, url: url) else {
|
||||
Issue.record("The remote CaskFile could not be parsed, check the log for more info")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -10,28 +10,32 @@ import Testing
|
||||
import Foundation
|
||||
|
||||
struct ExtensionEnumeratorTest {
|
||||
var container: Container
|
||||
|
||||
init() async throws {
|
||||
ActiveFileSystem.useTestable([
|
||||
"\(Paths.tapPath)/shivammathur/homebrew-extensions/Formula/xdebug@8.1.rb": .fake(.text, "<test>"),
|
||||
"\(Paths.tapPath)/shivammathur/homebrew-extensions/Formula/xdebug@8.2.rb": .fake(.text, "<test>"),
|
||||
"\(Paths.tapPath)/shivammathur/homebrew-extensions/Formula/xdebug@8.3.rb": .fake(.text, "<test>"),
|
||||
"\(Paths.tapPath)/shivammathur/homebrew-extensions/Formula/xdebug@8.4.rb": .fake(.text, "<test>")
|
||||
let paths = Paths(container: Container.fake())
|
||||
|
||||
container = Container.fake(files: [
|
||||
"\(paths.tapPath)/shivammathur/homebrew-extensions/Formula/xdebug@8.1.rb": .fake(.text, "<test>"),
|
||||
"\(paths.tapPath)/shivammathur/homebrew-extensions/Formula/xdebug@8.2.rb": .fake(.text, "<test>"),
|
||||
"\(paths.tapPath)/shivammathur/homebrew-extensions/Formula/xdebug@8.3.rb": .fake(.text, "<test>"),
|
||||
"\(paths.tapPath)/shivammathur/homebrew-extensions/Formula/xdebug@8.4.rb": .fake(.text, "<test>")
|
||||
])
|
||||
}
|
||||
|
||||
@Test func can_read_formulae() throws {
|
||||
let directory = "\(Paths.tapPath)/shivammathur/homebrew-extensions/Formula"
|
||||
let files = try FileSystem.getShallowContentsOfDirectory(directory)
|
||||
let directory = "\(container.paths.tapPath)/shivammathur/homebrew-extensions/Formula"
|
||||
let files = try container.filesystem.getShallowContentsOfDirectory(directory)
|
||||
|
||||
#expect(Set(files) == Set(["xdebug@8.1.rb", "xdebug@8.2.rb", "xdebug@8.3.rb", "xdebug@8.4.rb"]))
|
||||
}
|
||||
|
||||
@Test func can_parse_formulae_based_on_syntax() throws {
|
||||
let formulae = BrewTapFormulae.from(tap: "shivammathur/homebrew-extensions")
|
||||
let formulae = BrewTapFormulae.from(container, tap: "shivammathur/homebrew-extensions")
|
||||
|
||||
#expect(formulae["8.1"] == [BrewPhpExtension(path: "/", name: "xdebug", phpVersion: "8.1")])
|
||||
#expect(formulae["8.2"] == [BrewPhpExtension(path: "/", name: "xdebug", phpVersion: "8.2")])
|
||||
#expect(formulae["8.3"] == [BrewPhpExtension(path: "/", name: "xdebug", phpVersion: "8.3")])
|
||||
#expect(formulae["8.4"] == [BrewPhpExtension(path: "/", name: "xdebug", phpVersion: "8.4")])
|
||||
#expect(formulae["8.1"] == [BrewPhpExtension(container, path: "/", name: "xdebug", phpVersion: "8.1")])
|
||||
#expect(formulae["8.2"] == [BrewPhpExtension(container, path: "/", name: "xdebug", phpVersion: "8.2")])
|
||||
#expect(formulae["8.3"] == [BrewPhpExtension(container, path: "/", name: "xdebug", phpVersion: "8.3")])
|
||||
#expect(formulae["8.4"] == [BrewPhpExtension(container, path: "/", name: "xdebug", phpVersion: "8.4")])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,12 +53,12 @@ struct HomebrewPackageTest {
|
||||
/// or the JSON API of the Homebrew output may have changed.
|
||||
@Test(.disabled("Uses system command; enable at your own risk"))
|
||||
func can_parse_services_json_from_cli_output() async throws {
|
||||
ActiveShell.useSystem()
|
||||
let container = Container.real()
|
||||
|
||||
let services = try! JSONDecoder().decode(
|
||||
[HomebrewService].self,
|
||||
from: await Shell.pipe(
|
||||
"sudo \(Paths.brew) services info --all --json"
|
||||
from: await container.shell.pipe(
|
||||
"sudo \(container.paths.brew) services info --all --json"
|
||||
).out.data(using: .utf8)!
|
||||
).filter({ service in
|
||||
return ["php", "nginx", "dnsmasq"].contains(service.name)
|
||||
@@ -76,12 +76,11 @@ struct HomebrewPackageTest {
|
||||
/// or the JSON API of the Homebrew output may have changed.
|
||||
@Test(.disabled("Uses system command; enable at your own risk"))
|
||||
func can_load_extension_json_from_cli_output() async throws {
|
||||
|
||||
ActiveShell.useSystem()
|
||||
let container = Container.real()
|
||||
|
||||
let package = try! JSONDecoder().decode(
|
||||
[HomebrewPackage].self,
|
||||
from: await Shell.pipe("\(Paths.brew) info php --json").out.data(using: .utf8)!
|
||||
from: await container.shell.pipe("\(container.paths.brew) info php --json").out.data(using: .utf8)!
|
||||
).first!
|
||||
|
||||
#expect(package.full_name == "php")
|
||||
|
||||
@@ -10,42 +10,45 @@ import Testing
|
||||
import Foundation
|
||||
|
||||
struct HomebrewUpgradableTest {
|
||||
var container: Container
|
||||
init() throws {
|
||||
container = Container.fake(
|
||||
shell: [
|
||||
"/opt/homebrew/bin/brew update >/dev/null && /opt/homebrew/bin/brew outdated --json --formulae"
|
||||
: .instant(try! String(contentsOf: Self.outdatedFileUrl)),
|
||||
"/opt/homebrew/bin/php --ini | grep -E -o '(/[^ ]+\\.ini)'"
|
||||
: .instant("/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini"),
|
||||
"/opt/homebrew/opt/php@8.1.16/bin/php --ini | grep -E -o '(/[^ ]+\\.ini)'"
|
||||
: .instant("/opt/homebrew/etc/php/8.1/conf.d/php-memory-limits.ini"),
|
||||
"/opt/homebrew/opt/php@8.2.3/bin/php --ini | grep -E -o '(/[^ ]+\\.ini)'"
|
||||
: .instant("/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini"),
|
||||
"/opt/homebrew/opt/php@7.4.11/bin/php --ini | grep -E -o '(/[^ ]+\\.ini)'"
|
||||
: .instant("/opt/homebrew/etc/php/7.4/conf.d/php-memory-limits.ini")
|
||||
],
|
||||
files: [
|
||||
"/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini": .fake(.text),
|
||||
"/opt/homebrew/etc/php/8.1/conf.d/php-memory-limits.ini": .fake(.text),
|
||||
"/opt/homebrew/etc/php/7.4/conf.d/php-memory-limits.ini": .fake(.text)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
static var outdatedFileUrl: URL {
|
||||
return TestBundle.url(forResource: "brew-outdated", withExtension: "json")!
|
||||
}
|
||||
|
||||
@Test func upgradable_php_versions_can_be_determined() async throws {
|
||||
// Do not execute production cli commands
|
||||
ActiveShell.useTestable([
|
||||
"/opt/homebrew/bin/brew update >/dev/null && /opt/homebrew/bin/brew outdated --json --formulae"
|
||||
: .instant(try! String(contentsOf: Self.outdatedFileUrl)),
|
||||
"/opt/homebrew/bin/php --ini | grep -E -o '(/[^ ]+\\.ini)'"
|
||||
: .instant("/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini"),
|
||||
"/opt/homebrew/opt/php@8.1.16/bin/php --ini | grep -E -o '(/[^ ]+\\.ini)'"
|
||||
: .instant("/opt/homebrew/etc/php/8.1/conf.d/php-memory-limits.ini"),
|
||||
"/opt/homebrew/opt/php@8.2.3/bin/php --ini | grep -E -o '(/[^ ]+\\.ini)'"
|
||||
: .instant("/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini"),
|
||||
"/opt/homebrew/opt/php@7.4.11/bin/php --ini | grep -E -o '(/[^ ]+\\.ini)'"
|
||||
: .instant("/opt/homebrew/etc/php/7.4/conf.d/php-memory-limits.ini")
|
||||
])
|
||||
|
||||
// Do not read our production files
|
||||
ActiveFileSystem.useTestable([
|
||||
"/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini": .fake(.text),
|
||||
"/opt/homebrew/etc/php/8.1/conf.d/php-memory-limits.ini": .fake(.text),
|
||||
"/opt/homebrew/etc/php/7.4/conf.d/php-memory-limits.ini": .fake(.text)
|
||||
])
|
||||
|
||||
// This config file assumes our PHP alias (`php`) is v8.2
|
||||
PhpEnvironments.brewPhpAlias = "8.2"
|
||||
let env = App.shared.container.phpEnvs
|
||||
let env = container.phpEnvs!
|
||||
env.cachedPhpInstallations = [
|
||||
"8.1": PhpInstallation("8.1.16"),
|
||||
"8.2": PhpInstallation("8.2.3"),
|
||||
"7.4": PhpInstallation("7.4.11")
|
||||
"8.1": PhpInstallation(container, "8.1.16"),
|
||||
"8.2": PhpInstallation(container, "8.2.3"),
|
||||
"7.4": PhpInstallation(container, "7.4.11")
|
||||
]
|
||||
|
||||
let data = await BrewPhpFormulaeHandler().loadPhpVersions(loadOutdated: true)
|
||||
let data = await BrewPhpFormulaeHandler(container: container)
|
||||
.loadPhpVersions(loadOutdated: true)
|
||||
|
||||
#expect(true == data.contains(where: { formula in
|
||||
formula.installedVersion == "8.1.16" && formula.upgradeVersion == "8.1.17"
|
||||
|
||||
@@ -10,6 +10,11 @@ import Testing
|
||||
import Foundation
|
||||
|
||||
struct NginxConfigurationTest {
|
||||
var container: Container
|
||||
|
||||
init () async throws {
|
||||
container = Container.real()
|
||||
}
|
||||
|
||||
// MARK: - Test Files
|
||||
|
||||
@@ -36,33 +41,33 @@ struct NginxConfigurationTest {
|
||||
// MARK: - Tests
|
||||
|
||||
@Test func 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)
|
||||
#expect("nginx-site" == NginxConfigurationFile.from(container, filePath: Self.regularUrl.path)?.domain)
|
||||
#expect("test" == NginxConfigurationFile.from(container, filePath: Self.regularUrl.path)?.tld)
|
||||
}
|
||||
|
||||
@Test func can_determine_isolation() throws {
|
||||
#expect(nil == NginxConfigurationFile.from(filePath: Self.regularUrl.path)?.isolatedVersion)
|
||||
#expect("8.1" == NginxConfigurationFile.from(filePath: Self.isolatedUrl.path)?.isolatedVersion)
|
||||
#expect(nil == NginxConfigurationFile.from(container, filePath: Self.regularUrl.path)?.isolatedVersion)
|
||||
#expect("8.1" == NginxConfigurationFile.from(container, filePath: Self.isolatedUrl.path)?.isolatedVersion)
|
||||
}
|
||||
|
||||
@Test func can_determine_proxy() throws {
|
||||
let proxied = NginxConfigurationFile.from(filePath: Self.proxyUrl.path)!
|
||||
let proxied = NginxConfigurationFile.from(container, 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)!
|
||||
let normal = NginxConfigurationFile.from(container, filePath: Self.regularUrl.path)!
|
||||
#expect(false == normal.contents.contains("# valet stub: proxy.valet.conf"))
|
||||
#expect(nil == normal.proxy)
|
||||
}
|
||||
|
||||
@Test func can_determine_secured_proxy() throws {
|
||||
let proxied = NginxConfigurationFile.from(filePath: Self.secureProxyUrl.path)!
|
||||
let proxied = NginxConfigurationFile.from(container, 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 can_determine_proxy_with_custom_tld() throws {
|
||||
let proxied = NginxConfigurationFile.from(filePath: Self.customTldProxyUrl.path)!
|
||||
let proxied = NginxConfigurationFile.from(container, filePath: Self.customTldProxyUrl.path)!
|
||||
#expect(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
|
||||
#expect("http://localhost:8080" == proxied.proxy)
|
||||
}
|
||||
|
||||
@@ -11,21 +11,25 @@ import Foundation
|
||||
|
||||
@Suite(.serialized)
|
||||
class PhpConfigurationFileTest {
|
||||
var container: Container
|
||||
|
||||
init() {
|
||||
self.container = Container.real()
|
||||
}
|
||||
|
||||
static var phpIniFileUrl: URL {
|
||||
return TestBundle.url(forResource: "php", withExtension: "ini")!
|
||||
}
|
||||
|
||||
@Test func can_load_extension() throws {
|
||||
ActiveFileSystem.useSystem()
|
||||
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)
|
||||
let iniFile = PhpConfigurationFile.from(container, filePath: Self.phpIniFileUrl.path)
|
||||
|
||||
#expect(iniFile != nil)
|
||||
#expect(!iniFile!.extensions.isEmpty)
|
||||
}
|
||||
|
||||
@Test func can_check_key_existence() throws {
|
||||
print(Self.phpIniFileUrl.path)
|
||||
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)!
|
||||
let iniFile = PhpConfigurationFile.from(container, filePath: Self.phpIniFileUrl.path)!
|
||||
|
||||
#expect(iniFile.has(key: "error_reporting"))
|
||||
#expect(iniFile.has(key: "display_errors"))
|
||||
@@ -33,7 +37,7 @@ class PhpConfigurationFileTest {
|
||||
}
|
||||
|
||||
@Test func can_check_key_value() throws {
|
||||
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)!
|
||||
let iniFile = PhpConfigurationFile.from(container, filePath: Self.phpIniFileUrl.path)!
|
||||
|
||||
#expect(iniFile.get(for: "error_reporting") != nil)
|
||||
#expect(iniFile.get(for: "error_reporting") == "E_ALL")
|
||||
@@ -46,8 +50,7 @@ class PhpConfigurationFileTest {
|
||||
let destination = Utility
|
||||
.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
|
||||
|
||||
let configurationFile = PhpConfigurationFile
|
||||
.from(filePath: destination.path)!
|
||||
let configurationFile = PhpConfigurationFile.from(container, filePath: destination.path)!
|
||||
|
||||
// 0. Verify the original value
|
||||
#expect(configurationFile.get(for: "error_reporting") == "E_ALL")
|
||||
|
||||
@@ -11,8 +11,10 @@ import Foundation
|
||||
|
||||
@Suite(.serialized)
|
||||
struct PhpExtensionTest {
|
||||
var container: Container
|
||||
|
||||
init () async throws {
|
||||
ActiveShell.useSystem()
|
||||
container = Container.real()
|
||||
}
|
||||
|
||||
static var phpIniFileUrl: URL {
|
||||
@@ -20,13 +22,13 @@ struct PhpExtensionTest {
|
||||
}
|
||||
|
||||
@Test func can_load_extension() throws {
|
||||
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
|
||||
let extensions = PhpExtension.from(container, filePath: Self.phpIniFileUrl.path)
|
||||
|
||||
#expect(!extensions.isEmpty)
|
||||
}
|
||||
|
||||
@Test func extension_name_is_correct() throws {
|
||||
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
|
||||
let extensions = PhpExtension.from(container, filePath: Self.phpIniFileUrl.path)
|
||||
|
||||
let extensionNames = extensions.map { (ext) -> String in
|
||||
return ext.name
|
||||
@@ -45,7 +47,7 @@ struct PhpExtensionTest {
|
||||
}
|
||||
|
||||
@Test func extension_status_is_correct() throws {
|
||||
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
|
||||
let extensions = PhpExtension.from(container, filePath: Self.phpIniFileUrl.path)
|
||||
|
||||
// xdebug should be enabled
|
||||
#expect(extensions[0].enabled == true)
|
||||
@@ -56,7 +58,7 @@ struct PhpExtensionTest {
|
||||
|
||||
@Test func toggle_works_as_expected() async throws {
|
||||
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
|
||||
let extensions = PhpExtension.from(filePath: destination.path)
|
||||
let extensions = PhpExtension.from(container, filePath: destination.path)
|
||||
#expect(extensions.count == 6)
|
||||
|
||||
// Try to disable xdebug (should be detected first)!
|
||||
@@ -71,6 +73,6 @@ struct PhpExtensionTest {
|
||||
#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)
|
||||
#expect(PhpExtension.from(container, filePath: destination.path).first!.enabled == false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ struct RealFileSystemTest {
|
||||
"\(temporaryDirectory)/brew/etc/lib/c"
|
||||
)
|
||||
|
||||
let contents = try! FileSystem.getShallowContentsOfDirectory("\(temporaryDirectory)/brew/etc/lib/c")
|
||||
let contents = try! filesystem.getShallowContentsOfDirectory("\(temporaryDirectory)/brew/etc/lib/c")
|
||||
#expect([] == contents)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@ import Foundation
|
||||
|
||||
@Suite(.serialized)
|
||||
struct TestableFileSystemTest {
|
||||
var container: Container
|
||||
init() throws {
|
||||
ActiveFileSystem.useTestable([
|
||||
container = Container.fake(files: [
|
||||
"/home/user/bin/foo": .fake(.binary),
|
||||
"/home/user/docs": .fake(.symlink, "/home/user/documents"),
|
||||
"/home/user/documents/script.sh": .fake(.text, "echo 'cool';"),
|
||||
@@ -22,6 +23,10 @@ struct TestableFileSystemTest {
|
||||
])
|
||||
}
|
||||
|
||||
var FileSystem: FileSystemProtocol {
|
||||
return container.filesystem
|
||||
}
|
||||
|
||||
@Test func testable_fs_is_in_use() {
|
||||
#expect(FileSystem is TestableFileSystem)
|
||||
}
|
||||
|
||||
@@ -11,10 +11,15 @@ import Foundation
|
||||
|
||||
@Suite(.serialized)
|
||||
struct RealShellTest {
|
||||
var container: Container
|
||||
|
||||
init() async throws {
|
||||
// Reset to the default shell
|
||||
ActiveShell.useSystem()
|
||||
container = Container.real()
|
||||
}
|
||||
|
||||
var Shell: ShellProtocol {
|
||||
return container.shell
|
||||
}
|
||||
|
||||
@Test func system_shell_is_default() async {
|
||||
|
||||
@@ -63,8 +63,8 @@ struct TestableShellTest {
|
||||
}
|
||||
|
||||
@Test func fake_shell_has_path() {
|
||||
ActiveShell.useTestable([:])
|
||||
let container = Container.fake()
|
||||
|
||||
#expect(Shell.PATH == "/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin")
|
||||
#expect(container.shell.PATH == "/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import Foundation
|
||||
|
||||
struct TestableConfigurationTest {
|
||||
@Test func configuration_can_be_saved_as_json() async {
|
||||
let container = Container.real()
|
||||
|
||||
// WORKING
|
||||
var configuration = TestableConfigurations.working
|
||||
|
||||
@@ -39,8 +41,8 @@ struct TestableConfigurationTest {
|
||||
)
|
||||
|
||||
// Verify that the files were written to disk
|
||||
#expect(FileSystem.fileExists(NSHomeDirectory() + "/.phpmon_fconf_working.json"))
|
||||
#expect(FileSystem.fileExists(NSHomeDirectory() + "/.phpmon_fconf_working_no_valet.json"))
|
||||
#expect(FileSystem.fileExists(NSHomeDirectory() + "/.phpmon_fconf_broken.json"))
|
||||
#expect(container.filesystem.fileExists(NSHomeDirectory() + "/.phpmon_fconf_working.json"))
|
||||
#expect(container.filesystem.fileExists(NSHomeDirectory() + "/.phpmon_fconf_working_no_valet.json"))
|
||||
#expect(container.filesystem.fileExists(NSHomeDirectory() + "/.phpmon_fconf_broken.json"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
import XCTest
|
||||
|
||||
class PhpVersionDetectionTest: XCTestCase {
|
||||
|
||||
func test_can_detect_valid_php_versions() async throws {
|
||||
let outcome = await App.shared.container.phpEnvs.extractPhpVersions(
|
||||
let container = Container.real()
|
||||
|
||||
let outcome = await container.phpEnvs.extractPhpVersions(
|
||||
from: [
|
||||
"", // empty lines should be omitted
|
||||
"php@8.0",
|
||||
|
||||
Reference in New Issue
Block a user