Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor #6

Merged
merged 3 commits into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading