提交 dee2ae99 编写于 作者: Huan (李卓桓)'s avatar Huan (李卓桓)

better watchDog reset, code cleanup

上级 d931da79
...@@ -17,7 +17,7 @@ const log = require('./npmlog-env') ...@@ -17,7 +17,7 @@ const log = require('./npmlog-env')
class Bridge { class Bridge {
constructor(options) { constructor(options) {
if (!options || !options.puppet) { throw new Error('Bridge need a puppet')} if (!options || !options.puppet) { throw new Error('Bridge need a puppet')}
log.verbose('PuppetwebBridge', 'new Bridge({puppet: %s, port: %s})' log.verbose('PuppetWebBridge', 'new Bridge({puppet: %s, port: %s})'
, options.puppet.constructor.name , options.puppet.constructor.name
, options.port) , options.port)
...@@ -27,23 +27,31 @@ class Bridge { ...@@ -27,23 +27,31 @@ class Bridge {
toString() { return `Bridge({puppet: ${this.options.puppet.constructor.name}, port: ${this.options.port}})` } toString() { return `Bridge({puppet: ${this.options.puppet.constructor.name}, port: ${this.options.port}})` }
init() { init() {
log.verbose('PuppetwebBridge', 'init()') log.verbose('PuppetWebBridge', 'init()')
return this.inject() return this.inject()
.then(r => {
log.verbose('PuppetWebBridge', 'init() inject() return %s', r)
return this
})
.catch(e => {
log.error('PuppetWebBridge', 'init() inject() exception: %s', e.message)
throw e
})
} }
logout() { logout() {
log.verbose('PuppetwebBridge', 'quit()') log.verbose('PuppetWebBridge', 'quit()')
return this.proxyWechaty('logout') return this.proxyWechaty('logout')
.catch(e => { .catch(e => {
log.error('PuppetwebBridge', 'logout() exception: %s', e.message) log.error('PuppetWebBridge', 'logout() exception: %s', e.message)
throw e throw e
}) })
} }
quit() { quit() {
log.verbose('PuppetwebBridge', 'quit()') log.verbose('PuppetWebBridge', 'quit()')
return this.proxyWechaty('quit') return this.proxyWechaty('quit')
.catch(e => { .catch(e => {
log.error('PuppetwebBridge', 'quit() exception: %s', e.message) log.error('PuppetWebBridge', 'quit() exception: %s', e.message)
throw e throw e
}) })
} }
...@@ -56,7 +64,7 @@ class Bridge { ...@@ -56,7 +64,7 @@ class Bridge {
getUserName() { getUserName() {
return this.proxyWechaty('getUserName') return this.proxyWechaty('getUserName')
.catch(e => { .catch(e => {
log.error('PuppetwebBridge', 'getUserName() exception: %s', e.message) log.error('PuppetWebBridge', 'getUserName() exception: %s', e.message)
throw e throw e
}) })
} }
...@@ -64,7 +72,7 @@ class Bridge { ...@@ -64,7 +72,7 @@ class Bridge {
send(toUserName, content) { send(toUserName, content) {
return this.proxyWechaty('send', toUserName, content) return this.proxyWechaty('send', toUserName, content)
.catch(e => { .catch(e => {
log.error('PuppetwebBridge', 'send() exception: %s', e.message) log.error('PuppetWebBridge', 'send() exception: %s', e.message)
throw e throw e
}) })
} }
...@@ -79,23 +87,23 @@ class Bridge { ...@@ -79,23 +87,23 @@ class Bridge {
const timeout = max * (backoff * max) / 2 const timeout = max * (backoff * max) / 2
return retryPromise({ max: max, backoff: backoff }, function (attempt) { return retryPromise({ max: max, backoff: backoff }, function (attempt) {
log.silly('PuppetwebBridge', 'getContact() retryPromise: attampt %s/%s time for timeout %s' log.silly('PuppetWebBridge', 'getContact() retryPromise: attampt %s/%s time for timeout %s'
, attempt, max, timeout) , attempt, max, timeout)
return this.proxyWechaty('getContact', id) return this.proxyWechaty('getContact', id)
.then(r => { .then(r => {
if (!r) { if (!r) {
throw ('got empty return') throw new Error('got empty return')
} }
return r return r
}) })
.catch(e => { .catch(e => {
log.error('PuppetwebBridge', 'proxyWechaty(getContact, %s) exception: %s', id, e.message) log.error('PuppetWebBridge', 'proxyWechaty(getContact, %s) exception: %s', id, e.message)
throw e throw e
}) })
}.bind(this)) }.bind(this))
.catch(e => { .catch(e => {
log.error('PuppetwebBridge', 'retryPromise() getContact() finally FAIL: %s', e.message) log.error('PuppetWebBridge', 'retryPromise() getContact() finally FAIL: %s', e.message)
throw e throw e
}) })
///////////////////////////////// /////////////////////////////////
...@@ -110,17 +118,17 @@ class Bridge { ...@@ -110,17 +118,17 @@ class Bridge {
) )
} }
inject() { inject() {
log.verbose('PuppetwebBridge', 'inject()') log.verbose('PuppetWebBridge', 'inject()')
return co.call(this, function* () { return co.call(this, function* () {
const injectio = this.getInjectio() const injectio = this.getInjectio()
let r = yield this.execute(injectio, this.port) let r = yield this.execute(injectio, this.port)
log.verbose('PuppetwebBridge', 'inject() injected, got [%s]', r) log.verbose('PuppetWebBridge', 'inject() injected, got [%s]', r)
r = yield this.proxyWechaty('init') r = yield this.proxyWechaty('init')
log.verbose('PuppetwebBridge', 'inject() Wechaty.init() return: %s', r) log.verbose('PuppetWebBridge', 'inject() Wechaty.init() return: %s', r)
return r return r
}) })
.catch (e => { .catch (e => {
log.error('PuppetwebBridge', 'inject() exception: %s', e.message) log.error('PuppetWebBridge', 'inject() exception: %s', e.message)
throw e throw e
}) })
} }
...@@ -138,10 +146,10 @@ class Bridge { ...@@ -138,10 +146,10 @@ class Bridge {
const argsDecoded = `JSON.parse(decodeURIComponent(window.atob('${argsEncoded}')))` const argsDecoded = `JSON.parse(decodeURIComponent(window.atob('${argsEncoded}')))`
const wechatyScript = `return (typeof Wechaty !== 'undefined' && Wechaty.${wechatyFunc}.apply(undefined, ${argsDecoded}))` const wechatyScript = `return (typeof Wechaty !== 'undefined' && Wechaty.${wechatyFunc}.apply(undefined, ${argsDecoded}))`
log.silly('PuppetwebBridge', 'proxyWechaty(%s, ...args) %s', wechatyFunc, wechatyScript) log.silly('PuppetWebBridge', 'proxyWechaty(%s, ...args) %s', wechatyFunc, wechatyScript)
return this.execute(wechatyScript) return this.execute(wechatyScript)
.catch(e => { .catch(e => {
log.error('PuppetwebBridge', 'proxyWechaty() exception: %s', e.message) log.warn('PuppetWebBridge', 'proxyWechaty() exception: %s', e.message || e)
throw e throw e
}) })
} }
...@@ -149,7 +157,7 @@ class Bridge { ...@@ -149,7 +157,7 @@ class Bridge {
execute(script, ...args) { execute(script, ...args) {
return this.puppet.browser.execute(script, ...args) return this.puppet.browser.execute(script, ...args)
.catch(e => { .catch(e => {
log.error('PuppetwebBridge', 'execute() exception: %s', e.message) log.warn('PuppetWebBridge', 'execute() exception: %s', e.message || e)
throw e throw e
}) })
} }
......
...@@ -85,7 +85,7 @@ class Browser extends EventEmitter { ...@@ -85,7 +85,7 @@ class Browser extends EventEmitter {
.catch(e => { .catch(e => {
log.error('PuppetWebBrowser', 'open() exception: %s', e.message) log.error('PuppetWebBrowser', 'open() exception: %s', e.message)
this.dead(e.message) this.dead(e.message)
throw e.message throw e
}) })
} }
...@@ -159,25 +159,39 @@ class Browser extends EventEmitter { ...@@ -159,25 +159,39 @@ class Browser extends EventEmitter {
, attempt, timeout) , attempt, timeout)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
require('ps-tree')(process.pid, (err, children) => { this.getBrowserPids()
if (err) { .then(pids => {
return reject(err) if (pids.length === 0) {
} resolve('clean')
const num = children.filter(child => /phantomjs/i.test(child.COMMAND)).length
if (num==0) {
return resolve('clean')
} else { } else {
return reject('dirty') reject(new Error('dirty'))
} }
}) })
.catch(e => reject(e))
}) })
}) })
.catch(e => { .catch(e => {
log.error('PuppetWebBrowser', 'retryPromise failed: %s', e) log.error('PuppetWebBrowser', 'retryPromise failed: %s', e.message)
throw e throw e
}) })
} }
getBrowserPids() {
return new Promise((resolve, reject) => {
require('ps-tree')(process.pid, (err, children) => {
if (err) {
reject(err)
return
}
const pids = children.filter(child => /phantomjs/i.test(child.COMMAND))
.map(child => child.PID)
resolve(pids)
return
})
})
}
/** /**
* only wrap addCookies for convinience * only wrap addCookies for convinience
* *
...@@ -185,7 +199,7 @@ class Browser extends EventEmitter { ...@@ -185,7 +199,7 @@ class Browser extends EventEmitter {
* deleteCookie / getCookie / getCookies * deleteCookie / getCookie / getCookies
*/ */
addCookies(cookie) { addCookies(cookie) {
if (this.dead()) { return Promise.reject('addCookies() - browser dead')} if (this.dead()) { return Promise.reject(new Error('addCookies() - browser dead'))}
if (cookie.map) { if (cookie.map) {
return cookie.map(c => { return cookie.map(c => {
...@@ -211,7 +225,7 @@ class Browser extends EventEmitter { ...@@ -211,7 +225,7 @@ class Browser extends EventEmitter {
execute(script, ...args) { execute(script, ...args) {
//log.verbose('PuppetWebBrowser', `Browser.execute(${script})`) //log.verbose('PuppetWebBrowser', `Browser.execute(${script})`)
// log.verbose('PuppetWebBrowser', `Browser.execute() driver.getSession: %s`, util.inspect(this.driver.getSession())) // log.verbose('PuppetWebBrowser', `Browser.execute() driver.getSession: %s`, util.inspect(this.driver.getSession()))
if (this.dead()) { return Promise.reject('browser dead') } if (this.dead()) { return Promise.reject(new Error('browser dead')) }
return this.driver.executeScript.apply(this.driver, arguments) return this.driver.executeScript.apply(this.driver, arguments)
.catch(e => { .catch(e => {
...@@ -248,7 +262,7 @@ class Browser extends EventEmitter { ...@@ -248,7 +262,7 @@ class Browser extends EventEmitter {
checkSession(session) { checkSession(session) {
log.verbose('PuppetWebBrowser', `checkSession(${session})`) log.verbose('PuppetWebBrowser', `checkSession(${session})`)
if (this.dead()) { return Promise.reject('checkSession() - browser dead')} if (this.dead()) { return Promise.reject(new Error('checkSession() - browser dead'))}
return this.driver.manage().getCookies() return this.driver.manage().getCookies()
.then(cookies => { .then(cookies => {
...@@ -264,8 +278,8 @@ class Browser extends EventEmitter { ...@@ -264,8 +278,8 @@ class Browser extends EventEmitter {
cleanSession(session) { cleanSession(session) {
log.verbose('PuppetWebBrowser', `cleanSession(${session})`) log.verbose('PuppetWebBrowser', `cleanSession(${session})`)
if (this.dead()) { return Promise.reject('cleanSession() - browser dead')} if (this.dead()) { return Promise.reject(new Error('cleanSession() - browser dead'))}
if (!session) { return Promise.reject('cleanSession() no session') } if (!session) { return Promise.reject(new Error('cleanSession() no session')) }
const filename = session const filename = session
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
...@@ -279,9 +293,9 @@ class Browser extends EventEmitter { ...@@ -279,9 +293,9 @@ class Browser extends EventEmitter {
} }
saveSession(session) { saveSession(session) {
log.verbose('PuppetWebBrowser', `saveSession(${session})`) log.verbose('PuppetWebBrowser', `saveSession(${session})`)
if (this.dead()) { return Promise.reject('saveSession() - browser dead')} if (this.dead()) { return Promise.reject(new Error('saveSession() - browser dead'))}
if (!session) { return Promise.reject('saveSession() no session') } if (!session) { return Promise.reject(new Error('saveSession() no session')) }
const filename = session const filename = session
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
...@@ -321,16 +335,16 @@ class Browser extends EventEmitter { ...@@ -321,16 +335,16 @@ class Browser extends EventEmitter {
loadSession(session) { loadSession(session) {
log.verbose('PuppetWebBrowser', `loadSession(${session})`) log.verbose('PuppetWebBrowser', `loadSession(${session})`)
if (this.dead()) { return Promise.reject('loadSession() - browser dead')} if (this.dead()) { return Promise.reject(new Error('loadSession() - browser dead'))}
if (!session) { return Promise.reject('loadSession() no session') } if (!session) { return Promise.reject(new Error('loadSession() no session')) }
const filename = session const filename = session
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.readFile(filename, (err, jsonStr) => { fs.readFile(filename, (err, jsonStr) => {
if (err) { if (err) {
if (err) { log.silly('PuppetWebBrowser', 'loadSession(%s) skipped because error code: %s', session, err.code) } if (err) { log.silly('PuppetWebBrowser', 'loadSession(%s) skipped because error code: %s', session, err.code) }
return reject('error code:' + err.code) return reject(new Error('error code:' + err.code))
} }
const cookies = JSON.parse(jsonStr) const cookies = JSON.parse(jsonStr)
......
...@@ -23,7 +23,8 @@ return (function(port) { ...@@ -23,7 +23,8 @@ return (function(port) {
var Wechaty = { var Wechaty = {
glue: { glue: {
} // will be initialized by glueAngular() function // will be initialized by glueAngular() function
}
// glue funcs // glue funcs
, getLoginStatusCode: function() { return Wechaty.glue.loginScope.code } , getLoginStatusCode: function() { return Wechaty.glue.loginScope.code }
...@@ -41,14 +42,14 @@ return (function(port) { ...@@ -41,14 +42,14 @@ return (function(port) {
} }
// funcs // funcs
, init: init , init: init // initialize Wechaty @ Browser
, send: send , send: send // send message to wechat user
, clog: clog // Console log , clog: clog // log to Console
, slog: slog // log throw Socket IO , slog: slog // log to SocketIO
, log: log , log: log // log to both Console & SocketIO
, ding: ding , ding: ding // simple return 'dong'
, quit: quit , quit: quit // quit wechat
, emit: emit , emit: emit // send event to server
, getContact: getContact , getContact: getContact
, getUserName: getUserName , getUserName: getUserName
...@@ -116,8 +117,9 @@ return (function(port) { ...@@ -116,8 +117,9 @@ return (function(port) {
} }
function heartBeat() { function heartBeat() {
var TIMEOUT = 15000 // 15s
Wechaty.emit('ding', 'heartbeat@browser') Wechaty.emit('ding', 'heartbeat@browser')
setTimeout(heartBeat, 15000) setTimeout(heartBeat, TIMEOUT)
} }
function glueAngular() { function glueAngular() {
......
...@@ -125,11 +125,13 @@ class PuppetWeb extends Puppet { ...@@ -125,11 +125,13 @@ class PuppetWeb extends Puppet {
yield this.browser.init() yield this.browser.init()
yield this.browser.open(fastUrl) yield this.browser.open(fastUrl)
if (this.session) { if (this.session) {
yield this.browser.loadSession(this.session).catch(e => { // fail safe yield this.browser.loadSession(this.session)
log.verbose('PuppetWeb', 'browser.loadSession() exception: %s', e.message) .catch(e => { // fail safe
log.verbose('PuppetWeb', 'browser.loadSession() exception: %s', e.message || e)
}) })
} }
yield this.browser.open() yield this.browser.open()
return this.browser // follow func name meaning
}).catch(e => { }).catch(e => {
log.error('PuppetWeb', 'initBrowser() exception: %s', e.message) log.error('PuppetWeb', 'initBrowser() exception: %s', e.message)
throw e throw e
...@@ -184,11 +186,23 @@ class PuppetWeb extends Puppet { ...@@ -184,11 +186,23 @@ class PuppetWeb extends Puppet {
}) })
} }
onBrowserDead(data) { onBrowserDead(e) {
log.verbose('PuppetWeb', 'onBrowserDead(%s)', data) // because this function is async, so maybe entry more than one times.
// guard by variable: onBrowserDeadBusy to prevent entrance the 2nd time.
if (this.onBrowserDeadBusy) {
log.warn('PuppetWeb', 'onBrowserDead() Im busy, dont call me again before I return. this time will return and do nothing')
return
}
this.onBrowserDeadBusy = true
log.verbose('PuppetWeb', 'onBrowserDead(%s)', e.message || e)
if (!this.browser || !this.bridge) {
log.error('PuppetWeb', 'onBrowserDead() browser or bridge not found. do nothing')
return
}
return co.call(this, function* () { return co.call(this, function* () {
log.verbose('PuppetWeb', 'try to reborn browser') log.verbose('PuppetWeb', 'onBrowserDead() try to reborn browser')
yield this.browser.quit() yield this.browser.quit()
.catch(e => { // fail safe .catch(e => { // fail safe
...@@ -201,81 +215,43 @@ class PuppetWeb extends Puppet { ...@@ -201,81 +215,43 @@ class PuppetWeb extends Puppet {
yield this.bridge.init() yield this.bridge.init()
log.verbose('PuppetWeb', 'bridge re-inited') log.verbose('PuppetWeb', 'bridge re-inited')
const dong = yield this.ding()
if (/dong/i.test(dong)) {
log.verbose('PuppetWeb', 'ding() works well after reset')
} else {
log.warn('PuppetWeb', 'ding() get error return after reset: ' + dong)
}
}) })
.then(() => { .catch(e => { // Exception
log.verbose('PuppetWeb', 'onBrowserDead() new browser borned')
})
.catch(e => {
log.error('PuppetWeb', 'onBrowserDead() exception: %s', e.message) log.error('PuppetWeb', 'onBrowserDead() exception: %s', e.message)
throw e throw e
}) })
.then(() => { // Finally
log.verbose('PuppetWeb', 'onBrowserDead() new browser borned')
this.onBrowserDeadBusy = false
})
} }
// feed me in time(after 1st feed), or I'll restart system // feed me in time(after 1st feed), or I'll restart system
watchDog(data) { watchDog(data, options) {
log.verbose('PuppetWeb', 'watchDog(%s)', data) log.silly('PuppetWeb', 'watchDog(%s)', data)
const TIMEOUT = 60000 // 60s options = options || {}
const TIMEOUT = options.timeout || 60000 // 60s default. can be override in options
if (this.watchDogTimer) { if (this.watchDogTimer) {
clearTimeout(this.watchDogTimer) clearTimeout(this.watchDogTimer)
} }
this.watchDogTimer = setTimeout(this.recoverFromUnknownState.bind(this), TIMEOUT) this.watchDogTimer = setTimeout(() => {
const err = new Error('watchdog timeout after ' + Math.floor(TIMEOUT/1000) + ' seconds')
this.emit('error', err)
this.onBrowserDead(err)
}, TIMEOUT)
this.watchDogTimer.unref() // dont block quit this.watchDogTimer.unref() // dont block quit
} }
// recover system from unknown state
recoverFromUnknownState()
{
log.warn('PuppetWeb', 'recoverFromUnknownState()')
if (!this.browser || !this.bridge) {
log.error('PuppetWeb', 'recoverFromUnknownState() browser or bridge not found!')
return
}
// 1. check & reset browser(if needed)
if (this.browser.dead()) {
log.verbose('PuppetWeb', 'watchDogReset() browser.dead(), wait it to restore...')
return
}
// 2. check bridge
this.ding()
.then(dong => {
if (dong==='dong') {
log.warn('PuppetWeb', 'watchDogReset() ding() works well, whats wrong?')
}
})
.catch(e => {
log.error('PuppetWeb', 'watchDogReset() ding() exception: %s', e.message)
throw e
})
// 3. re-init bridge
this.bridge.inject()
.then(() => {
log.verbose('PuppetWeb', 'watchDogReset() bridge.inject() done')
})
.catch(e => {
log.error('PuppetWeb', 'watchDogReset() bridge.inject() exception: %s', e.message)
throw e
})
// 4. confirm bridge works well
this.ding()
.then(dong => {
if (dong!=='dong') {
log.error('PuppetWeb', 'watchDogReset() ding() return[%s] not `dong`', dong)
} else {
log.verbose('PuppetWeb', 'watchDogReset() ding() works well after reset')
}
})
.catch(e => {
log.error('PuppetWeb', 'watchDogReset() ding() after reset exception: %s', e.message)
throw e
})
}
onServerDing(data) { onServerDing(data) {
log.verbose('PuppetWeb', 'onServerDing(%s)', data) log.silly('PuppetWeb', 'onServerDing(%s)', data)
this.watchDog(data) this.watchDog(data)
} }
onServerScan(data) { onServerScan(data) {
...@@ -318,7 +294,7 @@ class PuppetWeb extends Puppet { ...@@ -318,7 +294,7 @@ class PuppetWeb extends Puppet {
} }
/** /**
* `unload` event is sent from js@browser to webserver via socketio * `unload` event is sent from js@browser to webserver via socketio
* after received `unload`, we should re-inject the Wechaty js code into browser. * after received `unload`, we should fix bridge by re-inject the Wechaty js code into browser.
* possible conditions: * possible conditions:
* 1. browser refresh * 1. browser refresh
* 2. browser navigated to a new url * 2. browser navigated to a new url
...@@ -425,7 +401,7 @@ class PuppetWeb extends Puppet { ...@@ -425,7 +401,7 @@ class PuppetWeb extends Puppet {
} }
reply(message, replyContent) { reply(message, replyContent) {
if (message.self()) { if (message.self()) {
return Promise.reject('will not to reply message of myself') return Promise.reject(new Error('will not to reply message of myself'))
} }
const m = new Message() const m = new Message()
...@@ -467,7 +443,7 @@ class PuppetWeb extends Puppet { ...@@ -467,7 +443,7 @@ class PuppetWeb extends Puppet {
ding(data) { ding(data) {
return this.bridge.proxyWechaty('ding', data) return this.bridge.proxyWechaty('ding', data)
.catch(e => { .catch(e => {
log.warn('PuppetWeb', 'ding(%s) rejected: %s', data, e.message) log.warn('PuppetWeb', 'ding(%s) rejected: %s', data, e.message || e)
throw e throw e
}) })
} }
......
const co = require('co') const co = require('co')
const util = require('util') const util = require('util')
const test = require('tap').test const test = require('tap').test
const retryPromise = require('retry-promise').default
const log = require('../src/npmlog-env') const log = require('../src/npmlog-env')
...@@ -10,36 +11,6 @@ const SESSION = 'unit-test-session.json' ...@@ -10,36 +11,6 @@ const SESSION = 'unit-test-session.json'
const PuppetWeb = require('../src/puppet-web') const PuppetWeb = require('../src/puppet-web')
function dingSocket(server) {
const maxTime = 60000 // 60s
const waitTime = 500
let totalTime = 0
return new Promise((resolve, reject) => {
log.verbose('TestPuppetWeb', 'dingSocket()')
return testDing()
function testDing() {
// log.silly('TestPuppetWeb', server.socketio)
if (!server.socketClient) {
totalTime += waitTime
if (totalTime > maxTime) {
return reject('timeout after ' + totalTime + 'ms')
}
log.silly('TestPuppetWeb', 'waiting socketClient to connect for ' + totalTime + '/' + maxTime + ' ms...')
setTimeout(testDing, waitTime)
return
}
//log.silly('TestPuppetWebServer', server.socketClient)
server.socketClient.once('dong', data => {
log.verbose('TestPuppetWeb', 'socket recv event dong: ' + data)
return resolve(data)
})
server.socketClient.emit('ding')
}
})
}
test('PuppetWeb smoke testing', function(t) { test('PuppetWeb smoke testing', function(t) {
let pw = new PuppetWeb({port: PORT, head: HEAD, session: SESSION}) let pw = new PuppetWeb({port: PORT, head: HEAD, session: SESSION})
t.ok(pw, 'new PuppetWeb') t.ok(pw, 'new PuppetWeb')
...@@ -103,39 +74,95 @@ test('Puppet Web server/browser communication', function(t) { ...@@ -103,39 +74,95 @@ test('Puppet Web server/browser communication', function(t) {
.then(t.end) .then(t.end)
}) })
.catch(e => { t.fail(e) }) // Exception .catch(e => { t.fail(e) }) // Exception
return
/////////////////////////////////////////////////////////////////////////////
function dingSocket(server) {
const maxTime = 60000 // 60s
const waitTime = 500
let totalTime = 0
return new Promise((resolve, reject) => {
log.verbose('TestPuppetWeb', 'dingSocket()')
return testDing()
function testDing() {
// log.silly('TestPuppetWeb', server.socketio)
if (!server.socketClient) {
totalTime += waitTime
if (totalTime > maxTime) {
return reject('timeout after ' + totalTime + 'ms')
}
log.silly('TestPuppetWeb', 'waiting socketClient to connect for ' + totalTime + '/' + maxTime + ' ms...')
setTimeout(testDing, waitTime)
return
}
//log.silly('TestPuppetWebServer', server.socketClient)
server.socketClient.once('dong', data => {
log.verbose('TestPuppetWeb', 'socket recv event dong: ' + data)
return resolve(data)
})
server.socketClient.emit('ding')
}
})
}
}) })
/* test('Puppet Web watchdog timer', function(t) {
false && test('Puppet Web promise version of server/browser communication', function(t) { const pw = new PuppetWeb({port: PORT, head: HEAD, session: SESSION})
pw = new PuppetWeb({port: PORT, head: HEAD, session: SESSION})
t.ok(pw, 'new PuppetWeb') t.ok(pw, 'new PuppetWeb')
pw.init() co(function* () {
.then(r => { yield pw.initBrowser()
t.pass('pw inited') yield pw.initBridge()
return dingSocket(pw.server) yield pw.bridge.quit().catch(e => {/* fail safe */})
}) yield pw.browser.quit().catch(e => {/* fail safe */})
.then(retSocket => {
t.equal(retSocket, 'dong', 'dingSocket got dong') pw.once('error', e => {
return true t.ok(/watchdog timeout/i.test(e), 'should emit error after watchdog timeout')
}) })
.catch(e => { // Reject
log.warn('TestPuppetWeb', 'error: %s', e) pw.watchDog('test', {timeout: 1})
t.fail(e)
throw e const dong = yield waitDing()
}) t.equal(dong, 'dong', 'should got dong from ding after watchdog reset')
.then(r => { // Finally 1
t.pass('dingSocket resolved')
return pw.quit()
}) })
.then(r => { // Finally 2 .catch(e => { // Exception
t.pass('pw.quit() resolved') t.fail(e.message || e)
t.end()
}) })
.catch(e => { .then(t.end) // Finally
t.fail(e)
throw e return
}) // Exception /////////////////////////////////////////////////////////////////////////////
function waitDing() {
const max = 30
const backoff = 100
// max = (2*totalTime/backoff) ^ (1/2)
// timeout = 11250 for {max: 15, backoff: 100}
// timeout = 45000 for {max: 30, backoff: 100}
const timeout = max * (backoff * max) / 2
return retryPromise({ max: max, backoff: backoff }, function (attempt) {
log.silly('TestPuppetWeb', 'waitDing() retryPromise: attampt %s/%s time for timeout %s'
, attempt, max, timeout)
return pw.ding()
.then(r => {
if (!r) {
throw new Error('got empty return')
}
return r
})
.catch(e => {
log.verbose('TestPuppetWeb', 'waitDing() exception: %s', e.message || e)
throw e
})
})
.catch(e => {
log.error('TestPuppetWeb', 'retryPromise() waitDing() finally FAIL: %s', e.message)
throw e
})
}
}) })
*/
...@@ -16,17 +16,6 @@ const PuppetWebBridge = require('../src/puppet-web-bridge') ...@@ -16,17 +16,6 @@ const PuppetWebBridge = require('../src/puppet-web-bridge')
const PORT = process.env.WECHATY_PORT || 58788 const PORT = process.env.WECHATY_PORT || 58788
const HEAD = process.env.WECHATY_HEAD || false const HEAD = process.env.WECHATY_HEAD || false
function driverProcessNum() {
return new Promise((resolve, reject) => {
require('ps-tree')(process.pid, (err, children) => {
if (err) { return reject(err) }
children.forEach(child => log.silly('TestingWebDriver', 'ps-tree: %s %s', child.PID, child.COMMAND))
const num = children.filter(child => /phantomjs/i.test(child.COMMAND)).length
return resolve(num)
})
})
}
test('WebDriver process create & quit test', function(t) { test('WebDriver process create & quit test', function(t) {
co(function* () { co(function* () {
const b = new PuppetWebBrowser({port: PORT, head: HEAD}) const b = new PuppetWebBrowser({port: PORT, head: HEAD})
...@@ -37,16 +26,16 @@ test('WebDriver process create & quit test', function(t) { ...@@ -37,16 +26,16 @@ test('WebDriver process create & quit test', function(t) {
yield b.open() yield b.open()
t.pass('opened') t.pass('opened')
let n = yield driverProcessNum() let pids = yield b.getBrowserPids()
t.ok(n > 0, 'driver process exist') t.ok(pids.length > 0, 'driver process exist')
// console.log(b.driver.getSession()) // console.log(b.driver.getSession())
yield b.quit() yield b.quit()
t.pass('quited') t.pass('quited')
n = yield driverProcessNum() pids = yield b.getBrowserPids()
t.equal(n, 0, 'no driver process after quit') t.equal(pids.length, 0, 'no driver process after quit')
}) })
.catch(e => { t.fail(e) }) .catch(e => { t.fail(e) })
.then(t.end.bind(t)) .then(t.end.bind(t))
...@@ -54,7 +43,6 @@ test('WebDriver process create & quit test', function(t) { ...@@ -54,7 +43,6 @@ test('WebDriver process create & quit test', function(t) {
return return
}) })
// XXX WTF with co module???
test('WebDriver smoke testing', function(t) { test('WebDriver smoke testing', function(t) {
const wb = new PuppetWebBrowser({port: PORT, head: HEAD}) const wb = new PuppetWebBrowser({port: PORT, head: HEAD})
t.ok(wb, 'Browser instnace') t.ok(wb, 'Browser instnace')
...@@ -66,7 +54,7 @@ test('WebDriver smoke testing', function(t) { ...@@ -66,7 +54,7 @@ test('WebDriver smoke testing', function(t) {
var driver // for help function `execute` var driver // for help function `execute`
co(function* () { co(function* () {
const m = yield driverProcessNum() const m = (yield wb.getBrowserPids()).length
t.equal(m, 0, 'driver process not exist before get()') t.equal(m, 0, 'driver process not exist before get()')
driver = yield wb.initDriver() driver = yield wb.initDriver()
...@@ -82,7 +70,7 @@ test('WebDriver smoke testing', function(t) { ...@@ -82,7 +70,7 @@ test('WebDriver smoke testing', function(t) {
yield driver.get('https://wx.qq.com/') yield driver.get('https://wx.qq.com/')
t.pass('driver url opened') t.pass('driver url opened')
const n = yield driverProcessNum() const n = (yield wb.getBrowserPids()).length
t.ok(n > 0, 'driver process exist after get()') t.ok(n > 0, 'driver process exist after get()')
const retAdd = yield execute('return 1+1') const retAdd = yield execute('return 1+1')
...@@ -104,64 +92,3 @@ test('WebDriver smoke testing', function(t) { ...@@ -104,64 +92,3 @@ test('WebDriver smoke testing', function(t) {
return driver.executeScript.apply(driver, arguments) return driver.executeScript.apply(driver, arguments)
} }
}) })
test('WebDriver WTF testing', function(t) {
const wb = new PuppetWebBrowser({port: PORT, head: HEAD})
t.ok(wb, 'Browser instnace')
const mockPuppet = {browser: wb}
const bridge = new PuppetWebBridge({puppet: mockPuppet, port: PORT})
t.ok(bridge, 'Bridge instnace')
var driver // for help function `execute`
var injectio
driverProcessNum()
.then(n => {
t.equal(n, 0, 'driver process not exist before get()')
return wb.initDriver()
})
.then(d => {
driver = d
t.ok(driver, 'driver inited')
return bridge.getInjectio()
})
.then(r => {
injectio = r
t.ok(injectio.length > 10, 'got injectio')
return driver.get('https://wx.qq.com/')
})
.then(r => {
t.pass('driver url opened')
return driverProcessNum()
})
.then(n => {
t.ok(n > 0, 'driver process exist after get()')
return execute('return 1+1')
})
.then(retAdd => {
t.equal(retAdd, 2, 'execute js in browser')
return execute(injectio, PORT)
})
.then(retInject => {
t.equal(retInject, 'Wechaty', 'injected wechaty')
})
.catch(e => t.fail('promise rejected. e:' + e)) // Rejected
.then(r => wb.quit()) // Finally 1
.then(r => t.end()) // Finally 2
.catch(e => t.fail('exception got:' + e)) // Exception
return
//////////////////////////////////
function execute() {
return driver.executeScript.apply(driver, arguments)
}
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册