Skip to content

Commit

Permalink
Merge pull request #6 from hoangatuan/Support-logs-with-color
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
hoangatuan authored Sep 30, 2023
2 parents 83564bc + 021ea5e commit 4ca9125
Show file tree
Hide file tree
Showing 13 changed files with 322 additions and 22 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/check-leaks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ jobs:

- run: xcodebuild -project MemoryLeaksCheck.xcodeproj -scheme MemoryLeaksCheck -destination 'platform=iOS Simulator,name=iPhone 14 Pro' CONFIGURATION_BUILD_DIR=$PWD/build

- name: Run UI tests
run: maestro test ./maestro/leaksCheckFlow.yaml

- name: Check for leaks
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion Dangerfile.leaksReport
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ message = read_message_from_file(message_file)

# Check if the message is not empty
if message && !message.empty?
fail("Discovery leaks:\n #{message}")
fail("Leaks detected:\n #{message}")
else
nil
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "leaksdetector"
BuildableName = "leaksdetector"
BlueprintName = "leaksdetector"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LeaksDetectorTests"
BuildableName = "LeaksDetectorTests"
BlueprintName = "LeaksDetectorTests"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LeaksDetector"
BuildableName = "LeaksDetector"
BlueprintName = "LeaksDetector"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LeaksDetectorTests"
BuildableName = "LeaksDetectorTests"
BlueprintName = "LeaksDetectorTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
viewDebuggingEnabled = "No">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "leaksdetector"
BuildableName = "leaksdetector"
BlueprintName = "leaksdetector"
ReferencedContainer = "container:">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "leaksdetector"
BuildableName = "leaksdetector"
BlueprintName = "leaksdetector"
ReferencedContainer = "container:">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
18 changes: 18 additions & 0 deletions LeaksDetector/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@
"revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531",
"version" : "1.2.3"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "025bcb1165deab2e20d4eaba79967ce73013f496",
"version" : "1.2.1"
}
},
{
"identity" : "swift-tools-support-core",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-tools-support-core.git",
"state" : {
"revision" : "3b13e439a341bbbfe0f710c7d1be37221745ef1a",
"version" : "0.6.1"
}
}
],
"version" : 2
Expand Down
10 changes: 8 additions & 2 deletions LeaksDetector/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import PackageDescription

let package = Package(
name: "LeaksDetector",
platforms: [
.macOS(.v10_15),
.iOS(.v15)
],
products: [
.executable(name: "leaksdetector", targets: ["LeaksDetector"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.0"),
.package(url: "https://github.com/JohnSundell/ShellOut.git", from: "2.0.0")
.package(url: "https://github.com/JohnSundell/ShellOut.git", from: "2.0.0"),
.package(url: "https://github.com/apple/swift-tools-support-core", from: "0.6.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand All @@ -19,7 +24,8 @@ let package = Package(
name: "LeaksDetector",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"ShellOut"
"ShellOut",
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core")
]
),
.testTarget(
Expand Down
14 changes: 12 additions & 2 deletions LeaksDetector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ Just need to run the script:
$ leaksdetector $PROGRAM_NAME $MAESTRO_PATH_FILE
```

## How to run local

1. Open `LeaksDetector.swift`, change `processName` and `uiFlowFilePath` to a hardcoded value
2. Run:

```bash
$ swift run
```

## How to release new executable file?

```bash
Expand All @@ -27,8 +36,8 @@ Just need to run the script:

## Todo

1. Change color for output message
2. Create protocol `UIExecutor` to support multiple kind of ui testing tools: Maestro, Appium, ...
1. Investigate how to integrate with XCUITest
2. Create protocol `UIExecutor` to support multiple kind of ui testing tools: Maestro, Appium, XCUITest ...

## Why Maestro?

Expand All @@ -39,5 +48,6 @@ Just need to run the script:
- https://www.fivestars.blog/articles/ultimate-guide-swift-executables/
- https://www.fivestars.blog/articles/a-look-into-argument-parser/
- https://www.swiftbysundell.com/articles/building-a-command-line-tool-using-the-swift-package-manager/
- https://www.avanderlee.com/swift/command-line-tool-package-manager/
- Split to multiple Danger instance: https://www.jessesquires.com/blog/2020/12/15/running-multiple-dangers/
- Passing params to Danger: https://github.com/danger/swift/issues/213
29 changes: 29 additions & 0 deletions LeaksDetector/Sources/LeaksDetector/Helpers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// File.swift
//
//
// Created by Hoang Anh Tuan on 30/09/2023.
//

import Foundation
import TSCBasic
import TSCUtility

let terminalController = TerminalController(stream: stdoutStream)

func log(
message: @autoclosure () -> String,
color: TerminalController.Color = .noColor,
needEndline: Bool = true,
isBold: Bool = false
) {
#if DEBUG
debugPrint(message())
#else
terminalController?.write(message(), inColor: color, bold: isBold)

if needEndline {
terminalController?.endLine()
}
#endif
}
41 changes: 27 additions & 14 deletions LeaksDetector/Sources/LeaksDetector/LeaksDetector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,74 @@
import Foundation
import ArgumentParser
import ShellOut
import TSCBasic
import TSCUtility

@main
struct LeaksDetector: ParsableCommand {

static let configuration = CommandConfiguration(
abstract: "This program wraps up the logic integrate leaks checking with your CI workflow"
)

#if DEBUG
private var processName = "MemoryLeaksCheck"
private var uiFlowFilePath = "/Users/hoanganhtuan/Desktop/MemoryLeaksCheck/maestro/leaksCheckFlow.yaml"
#else
@Argument(help: "The name of the running process")
private var processName: String

@Argument(help: "The path to the maestro ui testing yaml file")
private var uiFlowFilePath: String

@Flag(name: .long, help: "Show extra logging for debugging purposes")
private var verbose: Bool = false


#endif

// @Flag(name: .long, help: "Show extra logging for debugging purposes")
// private var verbose: Bool = false

private var memgraphPath = "~/Desktop/Leaks.memgraph"
private var regex: String = ".*(\\d+) leaks for (\\d+) total leaked bytes.*"

func run() throws {
debugPrint("Start looking for process with name: \(processName)... 🔎")
log(message: "Start looking for process with name: \(processName)... 🔎")

if !runningMaestro() { return }
if !generateMemgraph(for: processName) { return }

do {
try checkLeaks()
} catch {
debugPrint("Error occurs while checking for leaks")
log(message: "Error occurs while checking for leaks", color: .red)
}
}

private func runningMaestro() -> Bool {
debugPrint("Start running ui flow... 🎥")
log(message: "Start running ui flow... 🎥")
do {
try shellOut(to: "maestro test \(uiFlowFilePath)")
return true
} catch {
let error = error as! ShellOutError
debugPrint("❌ Something went wrong when trying to capture ui flow. \(error.message)")
log(message: "❌ Something went wrong when trying to capture ui flow. \(error.message)", color: .red)
return false
}
}

private func generateMemgraph(for processName: String) -> Bool {
do {
try shellOut(to: "leaks \(processName) --outputGraph=\(memgraphPath)")
debugPrint("Generate memgraph successfully for process 🚀")
log(message: "Generate memgraph successfully for process 🚀", color: .green)
return true
} catch {
debugPrint("❌ Can not find any process with name: \(processName)")
log(message: "❌ Can not find any process with name: \(processName)", color: .red)
return false
}
}

private func checkLeaks() throws {
do {
debugPrint("Start checking for leaks... ⚙️")
log(message: "Start checking for leaks... ⚙️")
try shellOut(to: "leaks", arguments: ["\(memgraphPath) -q"])
} catch {
let error = error as! ShellOutError
Expand All @@ -67,7 +80,7 @@ struct LeaksDetector: ParsableCommand {
let numberOfLeaks = getNumberOfLeaks(from: numberOfLeaksMessage)

if numberOfLeaks < 1 {
debugPrint("Scan successfully. Don't find any leaks in the program! ✅")
log(message: "Scan successfully. Don't find any leaks in the program! ✅", color: .green)
return
}

Expand All @@ -81,14 +94,14 @@ struct LeaksDetector: ParsableCommand {

// Cache memgraphfile if need

debugPrint("Generating reports... ⚙️")
log(message: "Founded leaks. Generating reports... ⚙️")
try shellOut(to: "bundle exec danger --dangerfile=Dangerfile.leaksReport --danger_id=LeaksReport")

debugPrint("Cleaning... 🧹")
log(message: "Cleaning... 🧹")
_ = try? shellOut(to: "rm \(memgraphPath)")
_ = try? shellOut(to: "rm \(fileName)")

debugPrint("Done ✅")
log(message: "Done ✅", color: .green)
}
}

Expand Down
Binary file modified LeaksDetector/leaksdetector
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>classNames</key>
<dict>
<key>LeaksCheckerUITests</key>
<dict>
<key>testExample()</key>
<dict>
<key>com.apple.dt.XCTMetric_Memory-Hoang-Anh-Tuan.MemoryLeaksCheck.physical</key>
<dict>
<key>baselineAverage</key>
<real>0.000000</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
<key>com.apple.dt.XCTMetric_Memory-Hoang-Anh-Tuan.MemoryLeaksCheck.physical_peak</key>
<dict>
<key>baselineAverage</key>
<real>0.000000</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
</dict>
</dict>
</dict>
</dict>
</plist>
Loading

0 comments on commit 4ca9125

Please sign in to comment.