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

[#4949] WIP: fix: transpile once only #4975

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 9 additions & 3 deletions packages/wdio-cucumber-framework/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,14 @@ class CucumberAdapter {
let result

try {
// we need tp ensure that we don't run the registration code
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change can potentially go away if we only ever have one Runner instance that we reuse across runs.

// multiple times, otherwise we will get duplicated transpilations
// we also need to make sure that we only require spec files once
// otherwise they will be transpiled each time they are required
if(!global.supportCodeLibrary) {
// indentation is deliberately off so the diff is minimal
this.registerRequiredModules()
Cucumber.supportCodeLibraryBuilder.reset(this.cwd)

/**
* wdio hooks should be added before spec files are loaded
*/
Expand All @@ -89,7 +94,8 @@ class CucumberAdapter {
*/
setUserHookNames(Cucumber.supportCodeLibraryBuilder.options)
Cucumber.setDefaultTimeout(this.cucumberOpts.timeout)
const supportCodeLibrary = Cucumber.supportCodeLibraryBuilder.finalize()
global.supportCodeLibrary = Cucumber.supportCodeLibraryBuilder.finalize()
}
baruchvlz marked this conversation as resolved.
Show resolved Hide resolved

/**
* gets current step data: `{ uri, feature, scenario, step, sourceLocation }`
Expand All @@ -102,7 +108,7 @@ class CucumberAdapter {
const runtime = new Cucumber.Runtime({
eventBroadcaster: this.eventBroadcaster,
options: this.cucumberOpts,
supportCodeLibrary,
supportCodeLibrary: global.supportCodeLibrary,
testCases: this.testCases
})

Expand Down
71 changes: 45 additions & 26 deletions packages/wdio-local-runner/src/worker.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import path from 'path'
import child from 'child_process'
import EventEmitter from 'events'

import logger from '@wdio/logger'
import Runner from '@wdio/runner'

import RunnerTransformStream from './transformStream'
import ReplQueue from './replQueue'
import RunnerStream from './stdStream'

Expand Down Expand Up @@ -52,7 +51,7 @@ export default class WorkerInstance extends EventEmitter {
* spawns process to kick of wdio-runner
*/
startProcess () {
const { cid, execArgv } = this
const { cid } = this
const argv = process.argv.slice(2)

const runnerEnv = Object.assign({}, process.env, this.config.runnerEnv, {
Expand All @@ -63,24 +62,50 @@ export default class WorkerInstance extends EventEmitter {
runnerEnv.WDIO_LOG_PATH = path.join(this.config.outputDir, `wdio-${cid}.log`)
}

log.info(`Start worker ${cid} with arg: ${argv}`)
const childProcess = this.childProcess = child.fork(path.join(__dirname, 'run.js'), argv, {
cwd: process.cwd(),
env: runnerEnv,
execArgv,
stdio: ['inherit', 'pipe', 'pipe', 'ipc']
})

childProcess.on('message', ::this._handleMessage)
childProcess.on('error', ::this._handleError)
childProcess.on('exit', ::this._handleExit)

/* istanbul ignore if */
if (!process.env.JEST_WORKER_ID) {
childProcess.stdout.pipe(new RunnerTransformStream(cid)).pipe(stdOutStream)
childProcess.stderr.pipe(new RunnerTransformStream(cid)).pipe(stdErrStream)
// This class emulates a child process. This is not here to stay;
// I did this in order to test my assumption that I can get rid of the multiple transpilations
// by using the same process. Unfortunately the whole of webdriverio is architected in a way that
// it uses inter-process communication via `process.send`. As I wanted to get a changeset for
// discussion up first, I decided to minimize the changes and just inject the fake child process
// into components that assume to be executed in a child process.
class FakeChildProcess {
constructor(worker) {
this.runner = new Runner(this)
this.worker = worker
this.runner.on('error', ({ name, message, stack }) => this.worker._handleError({
origin: 'worker',
name: 'error',
content: { name, message, stack }
}))
this.runner.on('exit', ::this.worker._handleExit)
}
send(m) {
baruchvlz marked this conversation as resolved.
Show resolved Hide resolved
if (!m || !m.command) {
this.worker._handleMessage(m)
return
}

log.info(`Run worker command: ${m.command}`)
this.runner[m.command](m).then(
(result) => this.worker._handleMessage({
origin: 'worker',
name: 'finisedCommand',
content: {
command: m.command,
result
}
}),
(e) => {
log.error(`Failed launching test session: ${e.stack}`)
process.exit(1)
}
)
}
}

log.info(`Start worker ${cid} with arg: ${argv}`)
const childProcess = this.childProcess = new FakeChildProcess(this)

return childProcess
}

Expand Down Expand Up @@ -137,17 +162,11 @@ export default class WorkerInstance extends EventEmitter {
}

_handleExit (exitCode) {
const { cid, childProcess, specs, retries } = this

/**
* delete process of worker
*/
delete this.childProcess
const { cid, specs, retries } = this
this.isBusy = false

log.debug(`Runner ${cid} finished with exit code ${exitCode}`)
this.emit('exit', { cid, exitCode, specs, retries })
childProcess.kill('SIGTERM')
}

/**
Expand Down
9 changes: 5 additions & 4 deletions packages/wdio-runner/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import { runHook, initialiseInstance, filterLogTypes, getInstancesData, attachTo
const log = logger('@wdio/runner')

export default class Runner extends EventEmitter {
constructor () {
constructor (target) {
super()
this.configParser = new ConfigParser()
this.sigintWasCalled = false
this.target = target || process
}

/**
Expand Down Expand Up @@ -76,13 +77,13 @@ export default class Runner extends EventEmitter {
automationProtocol: './protocol-stub'
}, caps) : undefined

this.reporter = new BaseReporter(this.config, this.cid, { ...caps })
this.reporter = new BaseReporter(this.config, this.cid, { ...caps }, this.target)
/**
* initialise framework
*/
this.framework = initialisePlugin(this.config.framework, 'framework')
this.framework = await this.framework.init(cid, this.config, specs, caps, this.reporter)
process.send({ name: 'testFrameworkInit', content: { cid, caps, specs, hasTests: this.framework.hasTests() } })
this.target.send({ name: 'testFrameworkInit', content: { cid, caps, specs, hasTests: this.framework.hasTests() } })
if (!this.framework.hasTests()) {
return this._shutdown(0)
}
Expand Down Expand Up @@ -141,7 +142,7 @@ export default class Runner extends EventEmitter {
*/
const { protocol, hostname, port, path, queryParams } = browser.options
const { isW3C, sessionId } = browser
process.send({
this.target.send({
origin: 'worker',
name: 'sessionStarted',
content: { sessionId, isW3C, protocol, hostname, port, path, queryParams, isMultiremote, instances }
Expand Down
7 changes: 4 additions & 3 deletions packages/wdio-runner/src/reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ const DEFAULT_SYNC_INTERVAL = 100 // 100ms
* to all these reporters
*/
export default class BaseReporter {
constructor (config, cid, caps) {
constructor (config, cid, caps, target) {
this.config = config
this.cid = cid
this.caps = caps
this.target = target

/**
* these configurations are not publicly documented as there should be no desire for it
Expand All @@ -43,7 +44,7 @@ export default class BaseReporter {
/**
* Send failure message (only once) in case of test or hook failure
*/
sendFailureMessage(e, payload)
sendFailureMessage(e, payload, this.target)

this.reporters.forEach((reporter) => reporter.emit(e, payload))
}
Expand Down Expand Up @@ -88,7 +89,7 @@ export default class BaseReporter {
*/
getWriteStreamObject (reporter) {
return {
write: /* istanbul ignore next */ (content) => process.send({
write: /* istanbul ignore next */ (content) => this.target.send({
origin: 'reporter',
name: reporter,
content
Expand Down
4 changes: 2 additions & 2 deletions packages/wdio-runner/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ export function filterLogTypes(excludeDriverLogs, driverLogTypes) {
* @param {string} e event
* @param {object} payload payload
*/
export function sendFailureMessage(e, payload) {
export function sendFailureMessage(e, payload, target) {
if (e === 'test:fail' || (e === 'hook:end' && payload.error && mochaAllHooks.some(hook => payload.title.startsWith(hook)))) {
process.send({
target.send({
origin: 'reporter',
name: 'printFailureMessage',
content: payload
Expand Down