Skip to content

Commit

Permalink
Add ContinuousServerClient.js, #143
Browse files Browse the repository at this point in the history
  • Loading branch information
zepumph committed Jun 1, 2022
1 parent 61d43c7 commit d8b9c7f
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 1 deletion.
18 changes: 17 additions & 1 deletion js/Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

const Gruntfile = require( '../../chipper/js/grunt/Gruntfile' );
const ContinuousServer = require( './server/ContinuousServer' );
const ContinuousServerClient = require( './server/ContinuousServerClient' );
const assert = require( 'assert' );
const grunt = require( 'grunt' ); // eslint-disable-line
const _ = require( 'lodash' ); // eslint-disable-line
const winston = require( 'winston' );
const QuickServer = require( './server/QuickServer' );
Expand Down Expand Up @@ -79,4 +79,20 @@ module.exports = grunt => {
server.startMainLoop();
}
);

grunt.registerTask(
'client-server',
'Launches puppeteer clients to run tests for CT with the following options:\n' +
'--clients=NUMBER : specify how many puppeteer clients to run with, defaults to 16\n',
() => {

// We don't finish! Don't tell grunt this...
grunt.task.current.async();

const server = new ContinuousServerClient( {
numberOfPuppeteers: grunt.option( 'clients' ) ? grunt.option( 'clients' ) : 16
} );
server.startMainLoop();
}
);
};
75 changes: 75 additions & 0 deletions js/server/ContinuousServerClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2022, University of Colorado Boulder

/**
* A Node script that handles multiple browser clients for Continuous Testing's server. This file uses Workers to kick
* off instances of Puppeteer that will load the continuous-loop. This file is hard coded to point to bayes via https,
* and will need to be updated if that URL is no longer correct.
*
* @author Michael Kauzmann (PhET Interactive Simulations)
*/

const _ = require( 'lodash' ); // eslint-disable-line require-statement-match
const path = require( 'path' );
const assert = require( 'assert' );
const { Worker } = require( 'worker_threads' ); // eslint-disable-line require-statement-match
const sleep = require( '../../../perennial/js/common/sleep' );

process.on( 'SIGINT', () => process.exit( 0 ) );

class ContinuousServerClient {
constructor( options ) {

options = {
rootDir: path.normalize( `${__dirname}/../../../` ),
numberOfPuppeteers: 16,
...options
};

// @public {string} - root of your GitHub working copy, relative to the name of the directory that the
// currently-executing script resides in
this.rootDir = options.rootDir;

this.numberOfPuppeteers = options.numberOfPuppeteers;
}

/**
* Kick off a worker, add it to a list, and when complete, remove it from that list
* @private
* @param {Worker[]} workerList
* @returns {Promise<unknown>}
*/
newClientWorker( workerList ) {
//first argument is filename of the worker
const worker = new Worker( `${this.rootDir}/aqua/js/server/puppeteerCTClient.js`, { argv: [ 'Bayes%20Puppeteer' ] } );
workerList.push( worker );
worker.on( 'message', message => { console.log( 'Message from puppeteerClient:', message ); } );
worker.on( 'error', e => { console.error( 'Error from puppeteerClient:', e ); } );
worker.on( 'exit', code => {
const index = _.indexOf( workerList, worker );
assert( index !== -1, 'worker must be in list' );
workerList.splice( index, 1 );
if ( code !== 0 ) {
console.error( `Worker stopped with exit code ${code}` );
}
} );
}

/**
* @public
*/
async startMainLoop() {

const workers = [];

while ( true ) { // eslint-disable-line
while ( workers.length < this.numberOfPuppeteers ) {
this.newClientWorker( workers );
}

// Check back in every 30 seconds to see if we need to restart any workers.
await sleep( 30000 );
}
}
}

module.exports = ContinuousServerClient;
32 changes: 32 additions & 0 deletions js/server/puppeteerCTClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2022, University of Colorado Boulder

/**
* Launch puppeteer and point it to CT running on bayes for 15 minutes.
* @author Michael Kauzmann (PhET Interactive Simulations)
*/

const assert = require( 'assert' );
const puppeteerLoad = require( '../../../perennial/js/common/puppeteerLoad' );
const { parentPort } = require( 'worker_threads' ); // eslint-disable-line require-statement-match

process.on( 'SIGINT', () => process.exit() );

( async () => {

assert( process.argv[ 2 ], 'usage: node puppeteerHelpCT {{SOME_IDENTIFIER_HERE}}' );
const url = `https://bayes.colorado.edu/continuous-testing/aqua/html/continuous-loop.html?id=${process.argv[ 2 ]}`;
const error = await puppeteerLoad( url, {
waitAfterLoad: 15 * 60 * 1000, // 15 minutes
allowedTimeToLoad: 120000,
puppeteerTimeout: 1000000000,

// A page error is what we are testing for. Don't fail the browser instance out when an assertion occurs
resolvePageErrors: false
} );
if ( error ) {
parentPort.postMessage( error );
}

// The worker didn't seem to exit without this line
process.exit( 0 );
} )();

0 comments on commit d8b9c7f

Please sign in to comment.