Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
7 changes: 7 additions & 0 deletions .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 77 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/SUITCase.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SUITCase"
BuildableName = "SUITCase"
BlueprintName = "SUITCase"
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 = "SUITCaseTests"
BuildableName = "SUITCaseTests"
BlueprintName = "SUITCaseTests"
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">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SUITCase"
BuildableName = "SUITCase"
BlueprintName = "SUITCase"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
47 changes: 47 additions & 0 deletions Docs/run_this_from_post_action.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Add the following script as Post-action to your Target's scheme in Test section, set needed values:

```sh
# set log file path where get_device_screenshots.sh script actions will be saved for debug purposes
LOG_FILE_PATH="$PROJECT_DIR/TestPostActions.log"
rm "$LOG_FILE_PATH"

# remote path - this path is valid if you install SUITCase as a remote package
SWIFT_PACKAGES_PATH="${BUILD_DIR%Build/*}SourcePackages/checkouts"
SCRIPT_PATH="$SWIFT_PACKAGES_PATH/suitcase/Scripts/get_device_screenshots.sh"
# local path - set manually if you add SUITCase as a local package
LOCAL_SCRIPT_PATH=""

if test -f "$SCRIPT_PATH"; then
echo "Script found at '$SCRIPT_PATH'" >> "$LOG_FILE_PATH"
else
echo "Script not found at '$SCRIPT_PATH'" >> "$LOG_FILE_PATH"

# try local path if it is set
if [[ -z $LOCAL_SCRIPT_PATH ]]; then
echo "Local path not set too, exiting" >> "$LOG_FILE_PATH"
exit
fi

SCRIPT_PATH=$LOCAL_SCRIPT_PATH

if test -f "$SCRIPT_PATH"; then
echo "Script found at '$SCRIPT_PATH'" >> "$LOG_FILE_PATH"
else
echo "Script not found at '$SCRIPT_PATH', exiting" >> "$LOG_FILE_PATH"
exit
fi
fi

# set path where to save test images retrieved from device
TEST_IMAGES_DESTINATION_PATH="$PROJECT_DIR/TestImages"

# set test images relative path inside application container without leading slash
# for instance FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("TestImages").path
TEST_IMAGES_SOURCE_PATH="Documents/TestImages"

# set tests target bundle id. This will be a runner app where images will be saved
TESTS_TARGET_BUNDLE_ID="com.suitcase.SUITCaseExampleAppUITests"

# run configured script
"$SCRIPT_PATH" "$TESTS_TARGET_BUNDLE_ID" "$TEST_IMAGES_SOURCE_PATH" "$TEST_IMAGES_DESTINATION_PATH" "$LOG_FILE_PATH"
```
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,43 @@ Compares the average colors of screenshots.
* You can also verify the average color without the reference screenshot by using `averageColorIs(_ uiColor: UIColor, tolerance: Double = 0.1)` \
`XCTAssert(app.buttons["Red Button"].averageColorIs(.red))`

## Experimental support for testing on real devices using `ifuse` library
Currently SUITCase is intended to be used mainly with iOS Simulator, because the latter allows seamless access to macOS filesystem (test screenshots can be saved directly to your Mac). This is not as easy when speaking about testing on real devices, because tests are being run on device filesystem which has no direct access to macOS filesystem.

However we can opt-in saving all screenshots during tests on device, mount xctrunner application container in macOS and this way copy screenshots from the testable device to Mac. This needs additional setup as follows

### Setup

1. Install [ifuse](https://github.com/libimobiledevice/ifuse) using Homebrew (based on [this](https://habr.com/ru/post/459888/) article)
* Install `osxfuse`
```
brew install osxfuse
```
* Install dependencies
```
brew uninstall --ignore-dependencies libimobiledevice
brew uninstall --ignore-dependencies usbmuxd
#If you never installed libimobiledevice and usbmuxd before
#skip above commands
brew install --HEAD usbmuxd
brew unlink usbmuxd
brew link usbmuxd
brew install --HEAD libimobiledevice
```
**Important**: If you've already installed stable `libimobiledevice` and `usbmuxd` versions remove them and install `dev` versions with `--HEAD` instead to avoid connection issues with iOS 12
* Install `ifuse`
```
brew install ifuse
```

2. Add [this script](Docs/run_this_from_post_action.md) as Post-action to your Target's scheme in Test section and configure it properly

3. Make sure you selected a real device for testing and use the following code snippet to enable the feature
```
let imagesFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("TestImages").path
SUITCase.screenshotComparisonImagesFolder = imagesFolder
deviceTestingEnabled = true
```

## License
SUITCase is the open-source software under [the MPL 2.0 license.](LICENSE)
48 changes: 48 additions & 0 deletions Scripts/get_device_screenshots.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/bash

#
# Add the following script as Post-action to your Target's scheme in Test section, set needed values:
#
# SCRIPT_PATH="$PROJECT_DIR/get_device_screenshots.sh"
# # set path where to save test images retrieved from device
# TEST_IMAGES_DESTINATION_PATH="$PROJECT_DIR/TestImages"
# # set test images relative path inside application container without leading slash
# # for instance FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("TestImages").path
# TEST_IMAGES_SOURCE_PATH="Documents/TestImages"
# # set tests target bundle id. This will be a runner app where images will be saved
# TESTS_TARGET_BUNDLE_ID="com.suitcase.SUITCaseExampleAppUITests"
# "$SCRIPT_PATH" "$TESTS_TARGET_BUNDLE_ID" "$TEST_IMAGES_SOURCE_PATH" "$TEST_IMAGES_DESTINATION_PATH"


LOG_FILE_PATH="$4"

MOUNT_DIR="$HOME/fuse_mount_point"
echo "Mount point $MOUNT_DIR" >> "$LOG_FILE_PATH"

mkdir "$MOUNT_DIR"

# get input

TESTS_TARGET_BUNDLE_ID="$1.xctrunner"
TEST_IMAGES_SOURCE_PATH="$MOUNT_DIR/$2"
TEST_IMAGES_DESTINATION_PATH="$3"

echo "Got following data:" >> "$LOG_FILE_PATH"
echo "-- Bundle id $TESTS_TARGET_BUNDLE_ID" >> "$LOG_FILE_PATH"
echo "-- Images source path $TEST_IMAGES_SOURCE_PATH" >> "$LOG_FILE_PATH"
echo "-- Images destination path $TEST_IMAGES_DESTINATION_PATH" >> "$LOG_FILE_PATH"

# mount app container

echo "(Re)mounting '$TESTS_TARGET_BUNDLE_ID'" >> "$LOG_FILE_PATH"
# remount container to avoid copying error, make it forcibly (with -f flag) to get updated mounted container
umount -f -v "$MOUNT_DIR" >> "$LOG_FILE_PATH" 2>&1
ifuse --debug --container $TESTS_TARGET_BUNDLE_ID "$MOUNT_DIR" >> "$LOG_FILE_PATH" 2>&1

# copy images

echo "Copying images from $TEST_IMAGES_SOURCE_PATH to $TEST_IMAGES_DESTINATION_PATH"
cp -r "$TEST_IMAGES_SOURCE_PATH" "$TEST_IMAGES_DESTINATION_PATH" >> "$LOG_FILE_PATH" 2>&1



2 changes: 1 addition & 1 deletion Sources/SUITCase/SUITCase+getImagePaths.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import XCTest
@available(iOS 12.0, *)
@available(tvOS 10.0, *)
extension SUITCase {
static var screenshotComparisonImagesFolder = ProcessInfo.processInfo.environment["IMAGES_DIR"]
public static var screenshotComparisonImagesFolder = ProcessInfo.processInfo.environment["IMAGES_DIR"]

/// The enumeration of possible reference screenshots naming strategies.
public enum ScreenshotComparisonNamingStrategies {
Expand Down
2 changes: 1 addition & 1 deletion Sources/SUITCase/SUITCase+verifyScreenshot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ extension SUITCase {
withThreshold customThreshold: Double? = nil,
withMethod method: SUITCaseMethod = SUITCaseMethodWithTolerance(),
withLabel label: String? = nil) throws {
guard UIDevice.isSimulator else {
if !UIDevice.isSimulator && !deviceTestingEnabled {
throw VerifyScreenshotError.notSimulator
}

Expand Down
2 changes: 2 additions & 0 deletions Sources/SUITCase/SUITCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ open class SUITCase: XCTestCase {
public var screenshotComparisonGlobalThreshold = 0.01
/// Changes current reference images naming strategy.
public var screenshotComparisonNamingStrategy = ScreenshotComparisonNamingStrategies.imageSize
/// Enables testing on real devices, is ignored when run on Simulator.
public var deviceTestingEnabled = false
}
4 changes: 2 additions & 2 deletions Sources/SUITCase/UIDevice+modelName.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ extension UIDevice {
}()

#if targetEnvironment(simulator)
static let isSimulator = true
public static let isSimulator = true
#else
static let isSimulator = false
public static let isSimulator = false
#endif
}