add unit test target with BrewParser extraction
Extract brew output parsing into BrewParser.swift (pure Swift, no AppKit) and add BrewBarTests target with 7 passing tests covering parseOutdated and parseVersion edge cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6f255380d0
commit
4ab3cc1e70
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
*.xcuserdatad/
|
*.xcuserdatad/
|
||||||
CLAUDE.local.md
|
CLAUDE.local.md
|
||||||
.claude/settings.local.json
|
.claude/settings.local.json
|
||||||
|
build/
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,19 @@
|
||||||
objectVersion = 77;
|
objectVersion = 77;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
87F2AA852FEE797F0014F9D6 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 87F2AA5A2FEE797F0014F9D6 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 87F2AA612FEE797F0014F9D6;
|
||||||
|
remoteInfo = BrewBar;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
87F2AA622FEE797F0014F9D6 /* BrewBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BrewBar.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
87F2AA622FEE797F0014F9D6 /* BrewBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BrewBar.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
87F2AA802FEE797F0014F9D6 /* BrewBarTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BrewBarTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
|
@ -29,6 +40,11 @@
|
||||||
path = BrewBar;
|
path = BrewBar;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
87F2AA812FEE797F0014F9D6 /* BrewBarTests */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = BrewBarTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
|
@ -39,6 +55,13 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
87F2AA822FEE797F0014F9D6 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
|
@ -46,6 +69,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
87F2AA642FEE797F0014F9D6 /* BrewBar */,
|
87F2AA642FEE797F0014F9D6 /* BrewBar */,
|
||||||
|
87F2AA812FEE797F0014F9D6 /* BrewBarTests */,
|
||||||
87F2AA632FEE797F0014F9D6 /* Products */,
|
87F2AA632FEE797F0014F9D6 /* Products */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -54,6 +78,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
87F2AA622FEE797F0014F9D6 /* BrewBar.app */,
|
87F2AA622FEE797F0014F9D6 /* BrewBar.app */,
|
||||||
|
87F2AA802FEE797F0014F9D6 /* BrewBarTests.xctest */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -83,6 +108,28 @@
|
||||||
productReference = 87F2AA622FEE797F0014F9D6 /* BrewBar.app */;
|
productReference = 87F2AA622FEE797F0014F9D6 /* BrewBar.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
87F2AA842FEE797F0014F9D6 /* BrewBarTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 87F2AA892FEE797F0014F9D6 /* Build configuration list for PBXNativeTarget "BrewBarTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
87F2AA832FEE797F0014F9D6 /* Sources */,
|
||||||
|
87F2AA822FEE797F0014F9D6 /* Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
87F2AA862FEE797F0014F9D6 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
87F2AA812FEE797F0014F9D6 /* BrewBarTests */,
|
||||||
|
);
|
||||||
|
name = BrewBarTests;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = BrewBarTests;
|
||||||
|
productReference = 87F2AA802FEE797F0014F9D6 /* BrewBarTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
|
|
@ -96,6 +143,10 @@
|
||||||
87F2AA612FEE797F0014F9D6 = {
|
87F2AA612FEE797F0014F9D6 = {
|
||||||
CreatedOnToolsVersion = 26.3;
|
CreatedOnToolsVersion = 26.3;
|
||||||
};
|
};
|
||||||
|
87F2AA842FEE797F0014F9D6 = {
|
||||||
|
CreatedOnToolsVersion = 26.3;
|
||||||
|
TestTargetID = 87F2AA612FEE797F0014F9D6;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 87F2AA5D2FEE797F0014F9D6 /* Build configuration list for PBXProject "BrewBar" */;
|
buildConfigurationList = 87F2AA5D2FEE797F0014F9D6 /* Build configuration list for PBXProject "BrewBar" */;
|
||||||
|
|
@ -113,6 +164,7 @@
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
87F2AA612FEE797F0014F9D6 /* BrewBar */,
|
87F2AA612FEE797F0014F9D6 /* BrewBar */,
|
||||||
|
87F2AA842FEE797F0014F9D6 /* BrewBarTests */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
@ -135,8 +187,23 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
87F2AA832FEE797F0014F9D6 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
87F2AA862FEE797F0014F9D6 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 87F2AA612FEE797F0014F9D6 /* BrewBar */;
|
||||||
|
targetProxy = 87F2AA852FEE797F0014F9D6 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
87F2AA6B2FEE79800014F9D6 /* Debug */ = {
|
87F2AA6B2FEE79800014F9D6 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
|
@ -352,6 +419,48 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
87F2AA872FEE797F0014F9D6 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = GLVUYYHFD9;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.6;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.MaxSoch.BrewBarTests";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BrewBar.app/Contents/MacOS/BrewBar";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
87F2AA882FEE797F0014F9D6 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = GLVUYYHFD9;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.6;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.MaxSoch.BrewBarTests";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BrewBar.app/Contents/MacOS/BrewBar";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
|
@ -373,6 +482,15 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
87F2AA892FEE797F0014F9D6 /* Build configuration list for PBXNativeTarget "BrewBarTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
87F2AA872FEE797F0014F9D6 /* Debug */,
|
||||||
|
87F2AA882FEE797F0014F9D6 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 87F2AA5A2FEE797F0014F9D6 /* Project object */;
|
rootObject = 87F2AA5A2FEE797F0014F9D6 /* Project object */;
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
setMenuBarIcon("brewbar-updating")
|
setMenuBarIcon("brewbar-updating")
|
||||||
|
|
||||||
runBrew("outdated") { output in
|
runBrew("outdated") { output in
|
||||||
let lines = output.split(separator: "\n").map { String($0) }
|
let lines = BrewParser.parseOutdated(output)
|
||||||
|
|
||||||
self.cachedOutdated = lines
|
self.cachedOutdated = lines
|
||||||
self.lastOutdatedFetch = Date()
|
self.lastOutdatedFetch = Date()
|
||||||
|
|
@ -304,7 +304,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
/// ✅ Brew version (correct)
|
/// ✅ Brew version (correct)
|
||||||
func refreshBrewVersion() {
|
func refreshBrewVersion() {
|
||||||
runBrew("--version") { output in
|
runBrew("--version") { output in
|
||||||
let firstLine = output.split(separator: "\n").first.map(String.init) ?? "Unknown"
|
let firstLine = BrewParser.parseVersion(output)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.versionItem.title = "🏷 \(firstLine)"
|
self.versionItem.title = "🏷 \(firstLine)"
|
||||||
|
|
|
||||||
13
BrewBar/BrewParser.swift
Normal file
13
BrewBar/BrewParser.swift
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum BrewParser {
|
||||||
|
static func parseOutdated(_ output: String) -> [String] {
|
||||||
|
output.split(separator: "\n")
|
||||||
|
.map { String($0) }
|
||||||
|
.filter { !$0.isEmpty }
|
||||||
|
}
|
||||||
|
|
||||||
|
static func parseVersion(_ output: String) -> String {
|
||||||
|
output.split(separator: "\n").first.map(String.init) ?? "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
39
BrewBarTests/BrewBarTests.swift
Normal file
39
BrewBarTests/BrewBarTests.swift
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
@testable import BrewBar
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class BrewParserTests: XCTestCase {
|
||||||
|
// MARK: - parseOutdated
|
||||||
|
|
||||||
|
func testParseOutdated_multiplePackages() {
|
||||||
|
let output = "wget\nffmpeg\ngit\n"
|
||||||
|
XCTAssertEqual(BrewParser.parseOutdated(output), ["wget", "ffmpeg", "git"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParseOutdated_emptyOutput() {
|
||||||
|
XCTAssertEqual(BrewParser.parseOutdated(""), [])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParseOutdated_singlePackage() {
|
||||||
|
XCTAssertEqual(BrewParser.parseOutdated("wget\n"), ["wget"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParseOutdated_trailingNewlinesIgnored() {
|
||||||
|
let output = "wget\n\n\n"
|
||||||
|
XCTAssertEqual(BrewParser.parseOutdated(output), ["wget"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - parseVersion
|
||||||
|
|
||||||
|
func testParseVersion_picksFirstLine() {
|
||||||
|
let output = "Homebrew 4.3.0\nHomebrew/homebrew-core (git revision abc123)\n"
|
||||||
|
XCTAssertEqual(BrewParser.parseVersion(output), "Homebrew 4.3.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParseVersion_emptyOutput() {
|
||||||
|
XCTAssertEqual(BrewParser.parseVersion(""), "Unknown")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParseVersion_singleLine() {
|
||||||
|
XCTAssertEqual(BrewParser.parseVersion("Homebrew 4.3.0"), "Homebrew 4.3.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue