Skip to content

Commit

Permalink
Merge branch 'release/0.1.2' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
enebin committed Jun 21, 2023
2 parents 5351bc0 + 62d9220 commit ea2fcc5
Show file tree
Hide file tree
Showing 79 changed files with 2,748 additions and 5,427 deletions.
59 changes: 59 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Unit testing and collect result infomations

on:
pull_request:
types: [synchronize]

concurrency:
group: unit-test-${{ github.head_ref }}
cancel-in-progress: true

jobs:
test:
runs-on: macOS-latest

steps:
- uses: actions/checkout@v3

- name: Select Xcode
run: sudo xcode-select -s '/Applications/Xcode.app/Contents/Developer'

- name: Prepare test
run: |
cd ./Scripts
ROOT_PATH="../"
ROOT_DIR_NAME=$(basename "$(cd "$ROOT_PATH" && pwd)")
if [ "$ROOT_DIR_NAME" != "Aespa" ]; then
echo "❌ Error: Script's not called in proper path."
exit 1
fi
# Generate mocks
chmod +x ./gen-mocks.sh
./gen-mocks.sh
- name: Test
run: |
ROOT_PATH="./"
# Now do test
DERIVED_DATA_PATH="./DerivedData"
PROJECT_NAME="TestHostApp"
TEST_SCHEME="Test"
xcodebuild test \
-verbose \
-project ${ROOT_PATH}/Tests/${PROJECT_NAME}.xcodeproj \
-scheme ${TEST_SCHEME} \
-destination 'platform=iOS Simulator,name=iPhone 14 Pro,OS=latest' \
-derivedDataPath ${DERIVED_DATA_PATH} \
-enableCodeCoverage YES \
| xcpretty --color \
|| exit 1
# Check if the tests failed
if [ $? -ne 0 ]; then
echo "❌ Error: Tests failed."
exit 1
fi
9 changes: 3 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

Aespa-test-env/cuckoo_generator
Scripts/cuckoo_generator
Scripts/run

Aespa-test-env/run

Tests/Cuckoo/cuckoo_generator

Tests/Cuckoo/run
Tests/Tests/Mock/GeneratedMocks.swift
43 changes: 43 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
disabled_rules:
- colon
- comma
- control_statement
- trailing_whitespace
- nesting
- type_body_length
- file_length
- no_fallthrough_only

opt_in_rules:
- missing_docs

analyzer_rules:
- explicit_self

included:
- Sources/*
excluded:
- Tests/*

# If true, SwiftLint will not fail if no lintable files are found.
allow_zero_lintable_files: false

force_cast: warning # implicitly
force_try:
severity: warning # explicitly
# implicitly
line_length: 120

type_name:
max_length: # warning and error
warning: 40
error: 50
allowed_symbols: ["_"]

identifier_name:
allowed_symbols: ["_"]

missing_docs:
included: Sources/*

reporter: "xcode"
31 changes: 31 additions & 0 deletions Scripts/gen-mocks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash

ROOT_PATH="../"
ROOT_DIR_NAME=$(basename "$(cd "$ROOT_PATH" && pwd)")
if [ "$ROOT_DIR_NAME" != "Aespa" ]; then
echo "❌ Error: Script's not called in proper path."
exit 1
fi

if [ ! -f run ]; then
curl -Lo run https://raw.githubusercontent.com/Brightify/Cuckoo/master/run && chmod +x run
fi

PROJECT_NAME="Aespa"
TESTER_NAME="TestHostApp"
PACKAGE_SOURCE_PATH="${ROOT_PATH}/Sources/Aespa"
OUTPUT_FILE="${ROOT_PATH}/Tests/Tests/Mock/GeneratedMocks.swift"
SWIFT_FILES=$(find "$PACKAGE_SOURCE_PATH" -type f -name "*.swift" -print0 | xargs -0)

echo "✅ Generated Mocks File = ${OUTPUT_FILE}"
echo "✅ Mocks Input Directory = ${PACKAGE_SOURCE_PATH}"

./run --download generate --testable "${PROJECT_NAME}" --output "${OUTPUT_FILE}" ${SWIFT_FILES}

# Check the exit status of the last command
if [ $? -ne 0 ]; then
echo "❌ Error: Failed to generate mocks."
exit 1
fi

echo "✅ Generating mock was successful"
26 changes: 14 additions & 12 deletions Sources/Aespa/Aespa.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,47 +9,49 @@
open class Aespa {
/// The core `AespaSession` that manages the actual video recording session.
private static var core: AespaSession?

/// Creates a new `AespaSession` with the given options.
///
/// - Parameters:
/// - option: The `AespaOption` to configure the session.
/// - Returns: The newly created `AespaSession`.
public static func session(with option: AespaOption) -> AespaSession {
let newCore = AespaSession(option: option)

core = newCore

// Check logging option
Logger.enableLogging = option.log.loggingEnabled

return newCore
}

/// Configures the `AespaSession` for recording.
/// Call this method to start the flow of data from the capture session’s inputs to its outputs.
///
/// This method ensures that necessary permissions are granted and the session is properly configured before starting.
/// If either the session isn't configured or the necessary permissions aren't granted, it throws an error.
/// This method ensures that necessary permissions are granted
/// and the session is properly configured before starting.
/// If either the session isn't configured or the necessary permissions aren't granted,
/// it throws an error.
///
/// - Warning: This method is synchronous and blocks until the session starts running or it fails,
/// which it reports by posting an `AVCaptureSessionRuntimeError` notification.
public static func configure() async throws {
guard let core = core else {
throw AespaError.session(reason: .notConfigured)
}

guard
case .permitted = await AuthorizationChecker.checkCaptureAuthorizationStatus()
else {
throw AespaError.permission(reason: .denied)
}

try core.startSession()

Logger.log(message: "Session is configured successfully")
}

/// Terminates the current `AespaSession`.
///
/// If a session has been started, it stops the session and releases resources.
Expand All @@ -58,7 +60,7 @@ open class Aespa {
guard let core = core else {
return
}

try core.terminateSession()
Logger.log(message: "Session is terminated successfully")
}
Expand Down
16 changes: 12 additions & 4 deletions Sources/Aespa/AespaError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public enum AespaError: LocalizedError {
case device(reason: DeviceErrorReason)
case permission(reason: PermissionErrorReason)
case album(reason: AlbumErrorReason)

case file(reason: FileErrorReason)

public var errorDescription: String? {
switch self {
case .session(reason: let reason):
Expand All @@ -23,6 +24,8 @@ public enum AespaError: LocalizedError {
return reason.rawValue
case .album(reason: let reason):
return reason.rawValue
case .file(reason: let reason):
return reason.rawValue
}
}
}
Expand All @@ -40,7 +43,7 @@ public extension AespaError {
case cannotFindDevice =
"Couldn't find device. Check if you've added device properly"
}

enum DeviceErrorReason: String {
case invalid =
"Unable to set up camera device. Please check camera usage permission."
Expand All @@ -53,12 +56,12 @@ public extension AespaError {
case unsupported =
"Unsupported device (supported on iPhone XR and later devices)"
}

enum PermissionErrorReason: String {
case denied =
"Cannot take a video because camera permissions are denied."
}

enum AlbumErrorReason: String {
case unabledToAccess =
"Unable to access album"
Expand All @@ -67,4 +70,9 @@ public extension AespaError {
case notVideoURL =
"Received URL is not a video type."
}

enum FileErrorReason: String {
case unableToFlatten =
"Cannot take a video because camera permissions are denied."
}
}
37 changes: 24 additions & 13 deletions Sources/Aespa/AespaOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,16 @@ public typealias FileNamingRule = () -> String
/// `AespaOption` allows customization of various aspects of the video recording process,
/// such as the video asset configuration, session settings and logging preferences.
public struct AespaOption {
/// `Asset` configuration object which encapsulates options related to the media assets such as
/// the album name, file extension and naming convention for the files.
public let asset: Asset

/// `Session` configuration object which holds the settings to be applied for the `Aespa` session,
/// such as auto video orientation.
public let session: Session

/// `Log` configuration object which determines the logging behaviour during the session,
/// such as enabling or disabling logging.
public let log: Log

/// Creates an `AespaOption` with specified album name and an option to enable logging.
Expand All @@ -29,7 +37,7 @@ public struct AespaOption {
session: Session(),
log: Log(loggingEnabled: enableLogging))
}

/// Creates an `AespaOption` with specified asset, session and log options.
///
/// - Parameters:
Expand All @@ -44,22 +52,23 @@ public struct AespaOption {
}

public extension AespaOption {
/// `Asset` provides options for configuring the video assets, such as the album name, file naming rule, and file extension.
/// `Asset` provides options for configuring the video assets,
/// such as the album name, file naming rule, and file extension.
struct Asset {
/// The name of the album where recorded videos will be saved.
let albumName: String

/// A `Boolean` flag that determines to use in-memory cache for `VideoFile`
///
/// It's set `true` by default.
let useVideoFileCache: Bool

/// The file extension for the recorded videos.
let fileNameHandler: FileNamingRule

/// The rule for naming video files.
let fileExtension: String

init(
albumName: String,
useVideoFileCache: Bool = true,
Expand All @@ -72,15 +81,17 @@ public extension AespaOption {
self.fileNameHandler = fileNameHandler
}
}

/// `Session` provides options for configuring the video recording session, such as automatic video orientation.

/// `Session` provides options for configuring the video recording session,
/// such as automatic video orientation.
struct Session {
/// A Boolean value that determines whether video orientation should be automatic.
var autoVideoOrientationEnabled: Bool = true
/// An `AVCaptureDevice.DeviceType` value that determines camera device. If not specified, the device is automatically selected.
/// An `AVCaptureDevice.DeviceType` value that determines camera device.
/// If not specified, the device is automatically selected.
var cameraDevicePreference: AVCaptureDevice.DeviceType?
}

/// `Log` provides an option for enabling or disabling logging.
struct Log {
var loggingEnabled: Bool = true
Expand All @@ -96,19 +107,19 @@ public extension AespaOption {
var rule: FileNamingRule {
return { formatter.string(from: Date()) }
}

/// Creates a `Timestamp` file naming rule.
init() {
formatter = DateFormatter()
formatter.dateFormat = "yyyy_MM_dd_HH_mm_ss"
}
}

struct Random {
let rule: FileNamingRule = { UUID().uuidString }
}
}

/// `FileExtension` provides supported file extensions.
enum FileExtension: String {
case mp4
Expand Down
Loading

0 comments on commit ea2fcc5

Please sign in to comment.