Skip to content

Commit

Permalink
Merge pull request #12 from hoangatuan/Refactor-code
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
hoangatuan authored Jan 7, 2024
2 parents 0374d03 + 944c984 commit c0705f3
Show file tree
Hide file tree
Showing 18 changed files with 345 additions and 213 deletions.
15 changes: 3 additions & 12 deletions LeaksDetector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ A executable program to checks for leaks in a running program
Just need to run the script:

```bash
$ leaksdetector $PROGRAM_NAME $MAESTRO_PATH_FILE
$ leaksdetector maestro -p $PROGRAM_NAME -maestroFlowPath $MAESTRO_PATH_FILE -dangerFilePath $DANGER_FILE_PATH
```

## How to run local

1. Open `LeaksDetector.swift`, change `processName`, `executorType`, `maestroFlowPath`, `dangerPath` to a hardcoded value
1. Open `MaestroCommand.swift`, change `processName`, `maestroFlowPath`, `dangerPath` to a hardcoded value
2. Run:

```bash
Expand All @@ -31,18 +31,9 @@ Just need to run the script:
```bash
$ swift build -c release
$ cd .build/release
$ cp -f leaksdetector $LeaksDetectorPath
$ cp -f leaksdetector ./../../../
```

## Todo

1. Investigate how to integrate with XCUITest
2. Integrate with DocC

## Why Maestro?

- TBA

### References:

- https://www.fivestars.blog/articles/ultimate-guide-swift-executables/
Expand Down
70 changes: 70 additions & 0 deletions LeaksDetector/Sources/LeaksDetector/CommandSteps/CheckLeaks.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import Foundation
import ShellOut

enum LeaksCheckError: Error {
case leaksCommandFailed
case incorectOutputFormat
}

struct CheckLeaks: RunCommandStep {
let executor: Executor

private let regex: String = ".*(\\d+) leaks for (\\d+) total leaked bytes.*"

func run() throws {
do {
log(message: "Start checking for leaks... ⚙️")
let memgraphPath = executor.getMemgraphPath()

/// Running this script always throw error (somehow the leak tool throw error here) => So we need to process the memgraph in the `catch` block.
try shellOut(to: "leaks", arguments: ["\(memgraphPath) -q"])
} catch {
let error = error as! ShellOutError
if error.output.isEmpty {
log(message: "❌ Leaks command run failed! Please open an issue on Github for me to check!", color: .red)
throw LeaksCheckError.leaksCommandFailed
}

let inputs = error.output.components(separatedBy: "\n")
guard let numberOfLeaksMessage = inputs.first(where: { $0.matches(regex) }) else {
log(message: "❌ Generated leaks output is incorrect! Please open an issue on Github for me to check!", color: .red)
throw LeaksCheckError.incorectOutputFormat
}

let numberOfLeaks = getNumberOfLeaks(from: numberOfLeaksMessage)

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

for message in inputs {
let updatedMessage = "\"\(message)\""
try shellOut(to: "echo \(updatedMessage) >> \(Constants.leaksReportFileName)")
}

log(message: "Founded leaks. Generating reports... ⚙️")
}
}

private func getNumberOfLeaks(from message: String) -> Int {
if let regex = try? NSRegularExpression(pattern: regex, options: []) {
// Find the first match in the input string
if let match = regex.firstMatch(in: message, options: [], range: NSRange(message.startIndex..., in: message)) {
// Extract the "d" value from the first capture group
if let dRange = Range(match.range(at: 1), in: message), let dValue = Int(message[dRange]) {
return dValue
}
}
}

return 0
}
}
19 changes: 19 additions & 0 deletions LeaksDetector/Sources/LeaksDetector/CommandSteps/CleanUp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import Foundation
import ShellOut

struct CleanUp: RunCommandStep {
let executor: Executor

func run() throws {
log(message: "Cleaning... 🧹")
_ = try? shellOut(to: "rm \(executor.getMemgraphPath())")
_ = try? shellOut(to: "rm \(Constants.leaksReportFileName)")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import Foundation

struct GenerateMemgraphFile: RunCommandStep {
let executor: Executor
let processName: String

func run() throws {
do {
try executor.generateMemgraph(for: processName)
log(message: "Generate memgraph successfully for process 🚀", color: .green)
} catch let error {
log(message: "❌ Can not find any process with name: \(processName)", color: .red)
throw error
}
}
}
22 changes: 22 additions & 0 deletions LeaksDetector/Sources/LeaksDetector/CommandSteps/Report.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import Foundation
import ShellOut

struct DangerReportStep: RunCommandStep {
let dangerFilePath: String

func run() throws {
do {
try shellOut(to: "bundle exec danger --dangerfile=\(dangerFilePath) --danger_id=LeaksReport")
} catch let error {
log(message: "❌ Can not execute Danger", color: .red)
throw error
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import Foundation

protocol RunCommandStep {
func run() throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import ShellOut
import Foundation

struct SimulateUIFlow: RunCommandStep {
let executor: Executor

func run() throws {
log(message: "Start running ui flow... 🎥")
do {
try executor.simulateUI()
} catch let error {
let error = error as! ShellOutError
log(message: "❌ Something went wrong when trying to capture ui flow. \(error.message)", color: .red)
throw error
}
}
}
44 changes: 44 additions & 0 deletions LeaksDetector/Sources/LeaksDetector/Commands/MaestroCommand.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import ArgumentParser
import Foundation

struct MaestroCommand: ParsableCommand {
static let configuration = CommandConfiguration(
commandName: "maestro",
abstract: "Perform memory leaks check by using Maestro testing tool."
)

#if DEBUG
private var processName = "MemoryLeaksCheck"
private var maestroFlowPath: String = "/Users/hoanganhtuan/Desktop/MemoryLeaksCheck/maestro/leaksCheckFlow.yaml"
private var dangerFilePath: String = "Dangerfile.leaksReport"
#else
@Option(name: .shortAndLong, help: "The name of the running process")
private var processName: String

@Option(name: .long, help: "The path to the maestro ui testing yaml file")
private var maestroFlowPath: String

@Option(name: .long, help: "The path to the Dangerfile")
private var dangerFilePath: String?
#endif

public func run() throws {
let executor = MaestroExecutor(flowPath: maestroFlowPath)
let processor = Processor(
configuration: .init(
processName: processName,
dangerFilePath: dangerFilePath
),
executor: executor
)

processor.start()
}
}
53 changes: 53 additions & 0 deletions LeaksDetector/Sources/LeaksDetector/Commands/Processor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import Foundation

struct Configuration {
let processName: String
let dangerFilePath: String?
}

struct Processor {
let configuration: Configuration
let executor: Executor
let steps: [RunCommandStep]

init(configuration: Configuration, executor: Executor) {
self.configuration = configuration
self.executor = executor
var steps: [RunCommandStep] = [
SimulateUIFlow(executor: executor),
GenerateMemgraphFile(executor: executor, processName: configuration.processName),
CheckLeaks(executor: executor)
]

if let dangerFilePath = configuration.dangerFilePath {
steps.append(DangerReportStep(dangerFilePath: dangerFilePath))
}

steps.append(CleanUp(executor: executor))

self.steps = steps
}

private var processName: String {
configuration.processName
}

func start() {
do {
try steps.forEach { step in
try step.run()
}
log(message: "✅ The process finish successfully", color: .green)
} catch {
log(message: "❌ End the process with error", color: .red)
Darwin.exit(EXIT_FAILURE)
}
}
}
12 changes: 12 additions & 0 deletions LeaksDetector/Sources/LeaksDetector/Constants.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import Foundation

enum Constants {
static let leaksReportFileName = "leaksReport.txt"
}
39 changes: 0 additions & 39 deletions LeaksDetector/Sources/LeaksDetector/ExecutorFactory.swift

This file was deleted.

14 changes: 14 additions & 0 deletions LeaksDetector/Sources/LeaksDetector/Helpers/String+.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// File.swift
//
//
// Created by Tuan Hoang on 6/1/24.
//

import Foundation

extension String {
func matches(_ regex: String) -> Bool {
return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
}
}
Loading

0 comments on commit c0705f3

Please sign in to comment.