-
Notifications
You must be signed in to change notification settings - Fork 28
Plugins
Emcee runs plugins on its workers. Plugins do not alter any logic of Emcee, but they can listen to the events and react to them accordingly.
There are many use cases for creating your plugins. Some examples are:
-
Integrate your tests with your test management system: for example, update test statuses in your TMS when test finishes.
-
Record videos of tests: e.g. you may develop a logic to purge video if test succeeds, and upload video to your internal storage if test fails, for further investigation. Use
SimulatorVideoRecorder
module for that.
Tests can leave various artifacts in a place where plugin can pick them up later. Emcee creates a special folder and passes it into tests using EMCEE_TESTS_WORKING_DIRECTORY
environment variable.
Plugin can use TestsWorkingDirectoryDeterminer
class from TestsWorkingDirectorySupport
module to determine that path, and then pick up the artifacts left by tests.
It is up to you to determine the protocol between tests and the plugin. As an example, tests can create their reports and store them as JSON files, and plugin can then read these files and operate on them as needed. Tests and plugin can share underlying models to be in sync.
It is convenient to develop Emcee plugins as Swift packages.
This is a base module for implementing Emcee plugin. In your Package.swift
, import the library that has all required APIs to implement a plugin:
dependencies: [
// NOTE: it is better to use a fixed version instead of master branch
.package(url: "https://github.com/avito-tech/Emcee", .branch("master")),
]
In your plugin target add EmceePlugin
dependency:
targets: [
.target(
name: "TestPlugin",
dependencies: [
"EmceePlugin"
]
)
]
Plugin will listen to Emcee event bus via EventStream
instance. Emcee provides DefaultBusListener
open class for your convenience (available is EventBus
module). Subclass it and override its methods to receive corresponding events.
Example implementation of capturing commonly used events related to test execution flow.
import EmceeLogging
import EventBus
import Models
import Plugin
class MyPluginListener: DefaultBusListener {
private let logger: ContextualLogger
init(logger: ContextualLogger) {
// You can use emcee logging system
self.logger = logger
super.init()
}
override func runnerEvent(_ event: RunnerEvent) {
logger.trace("runnerEvent \(runnerEvent)")
switch event {
case .willRun(let testEntries, let testContext):
// Will be called when Emcee is preparing to run a set of tests
// from a single test bundle (bucket).
break
case .testStarted(let testEntry, let testContext):
// will be called when a single test has started
break
case .testFinished(let testEntry, let succeeded, let testContext):
// will be called when a previously started test has finished
break
case .didRun(let testEntryResults, let testContext):
// Will be called when Emcee finish running a set of tests
// and already has test results.
// This is a good place to process runner output (e.g. result bundle) if you need to.
break
}
}
override func tearDown() {
// will be called before Emcee will terminate your plugin
// close your pipes here, free up resources, store data
logger.trace("Plugin tear down")
}
}
Plugin can take advantage from TestContext
object passed into most events - it describes the test environment.
Since any plugin is a regular executable, you will need to provide main.swift
file - your executable entry point. It may contain the following code:
import EventBus
import Plugin
// Create an event bus that will get the events from the Emcee
let eventBus = EventBus()
// Create a plugin
let plugin = try Plugin(eventBus: eventBus)
// You can use Emcee logging by asking a logger from your new Plugin instance
// Messages will be logger to ~/Library/Logs/ru.avito.emcee.logs/.
// Note: calls to `print()` or other logging systems aren't not captured.
let logger = plugin.logger
logger.info("Started plugin")
// Subscribe to the event bus by providing your instance of EventStream
eventBus.add(
stream: MyPluginListener(
logger: logger
)
)
plugin.streamPluginEvents()
// Wait for plugin to finish
plugin.join()
First, build your plugin using swift build
command.
Emcee expects all plugins to have the following bundle structure:
YourPlugin.emceeplugin/
Plugin <-- your plugin executable
You can use some basic Bash skills to prepare bundle and ZIP it:
#!/bin/bash
set -e
pluginName="YourPlugin.emceeplugin"
pluginPath=".build/debug/$pluginName/"
rm -rf "$pluginPath"
mkdir -p "$pluginPath"
cp .build/debug/Plugin "$pluginPath"
cd "$pluginPath/../"
rm -rf "$pluginName".zip
zip -r "$pluginName".zip "$pluginName"
You should upload your YourPlugin.zip
to your web server. Then you can pass URL to this file in test arg file
.