Skip to content

Commit 026f928

Browse files
authored
Merge pull request #23 from browserstack/binary_download
Binary download
2 parents 34e9528 + 70a6daa commit 026f928

File tree

4 files changed

+181
-47
lines changed

4 files changed

+181
-47
lines changed

lib/Local.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var childProcess = require('child_process'),
2+
fs = require('fs'),
23
path = require('path'),
34
running = require('is-running'),
45
LocalBinary = require('./LocalBinary'),
@@ -7,16 +8,17 @@ var childProcess = require('child_process'),
78

89
function Local(){
910
this.pid = undefined;
11+
this.retriesLeft = 5;
1012
this.key = process.env.BROWSERSTACK_ACCESS_KEY;
1113
this.logfile = path.join(process.cwd(), 'local.log');
1214
this.opcode = 'start';
1315
this.exitCallback;
14-
this.userArgs = [];
1516

1617
this.errorRegex = /\*\*\* Error: [^\r\n]*/i;
1718
this.doneRegex = /Press Ctrl-C to exit/i;
1819

1920
this.start = function(options, callback){
21+
this.userArgs = [];
2022
var that = this;
2123
this.addArgs(options);
2224

@@ -29,7 +31,19 @@ function Local(){
2931

3032
that.opcode = 'start';
3133
that.tunnel = childProcess.execFile(that.binaryPath, that.getBinaryArgs(), function(error, stdout, stderr){
32-
if(error) callback(new LocalError(error.toString()));
34+
if(error) {
35+
console.error('Error while trying to execute binary', error);
36+
if(that.retriesLeft > 0) {
37+
console.log('Retrying Binary Download. Retries Left', that.retriesLeft);
38+
that.retriesLeft -= 1;
39+
fs.unlinkSync(that.binaryPath);
40+
delete(that.binaryPath);
41+
that.start(options, callback);
42+
return;
43+
} else {
44+
callback(new LocalError(error.toString()));
45+
}
46+
}
3347

3448
var data = {};
3549
if(stdout)
@@ -206,6 +220,7 @@ function Local(){
206220
}
207221
this.binary.binaryPath(conf, callback);
208222
} else {
223+
console.log('BINARY PATH IS DEFINED');
209224
callback(this.binaryPath);
210225
}
211226
};

lib/LocalBinary.js

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,56 @@ function LocalBinary(){
2222
this.httpPath = 'https://s3.amazonaws.com/browserStack/browserstack-local/BrowserStackLocal-linux-ia32';
2323
}
2424

25-
this.download = function(conf, destParentDir, callback){
25+
this.retryBinaryDownload = function(conf, destParentDir, callback, retries, binaryPath) {
26+
var that = this;
27+
if(retries > 0) {
28+
console.log('Retrying Download. Retries left', retries);
29+
fs.stat(binaryPath, function(err) {
30+
if(err == null) {
31+
fs.unlinkSync(binaryPath);
32+
}
33+
that.download(conf, destParentDir, callback, retries - 1);
34+
});
35+
} else {
36+
console.error('Number of retries to download exceeded.');
37+
}
38+
};
39+
40+
this.download = function(conf, destParentDir, callback, retries){
41+
var that = this;
2642
if(!this.checkPath(destParentDir))
2743
fs.mkdirSync(destParentDir);
2844

2945
var destBinaryName = (this.windows) ? 'BrowserStackLocal.exe' : 'BrowserStackLocal';
3046
var binaryPath = path.join(destParentDir, destBinaryName);
31-
var file = fs.createWriteStream(binaryPath);
47+
var fileStream = fs.createWriteStream(binaryPath);
3248

3349
var options = url.parse(this.httpPath);
34-
if(conf.proxyHost && conf.proxyPort){
50+
if(conf.proxyHost && conf.proxyPort) {
3551
options.agent = new HttpsProxyAgent({
3652
host: conf.proxyHost,
3753
port: conf.proxyPort
3854
});
3955
}
4056

4157
https.get(options, function (response) {
42-
response.on('end', function () {
58+
response.pipe(fileStream);
59+
response.on('error', function(err) {
60+
console.error('Got Error in binary download response', err);
61+
that.retryBinaryDownload(conf, destParentDir, callback, retries, binaryPath);
62+
});
63+
fileStream.on('error', function (err) {
64+
console.error('Got Error while downloading binary file', err);
65+
that.retryBinaryDownload(conf, destParentDir, callback, retries, binaryPath);
66+
});
67+
fileStream.on('close', function () {
4368
fs.chmod(binaryPath, '0755', function() {
4469
callback(binaryPath);
4570
});
4671
});
47-
response.pipe(file);
72+
}).on('error', function(err) {
73+
console.error('Got Error in binary downloading request', err);
74+
that.retryBinaryDownload(conf, destParentDir, callback, retries, binaryPath);
4875
});
4976
};
5077

@@ -55,7 +82,7 @@ function LocalBinary(){
5582
if(this.checkPath(binaryPath, fs.X_OK)){
5683
callback(binaryPath);
5784
} else {
58-
this.download(conf, destParentDir, callback);
85+
this.download(conf, destParentDir, callback, 5);
5986
}
6087
};
6188

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
"license": "MIT",
1919
"dependencies": {
2020
"https-proxy-agent": "^1.0.0",
21-
"is-running": "^2.0.0"
21+
"is-running": "^2.0.0",
22+
"sinon": "^1.17.6",
23+
"temp-fs": "^0.9.9"
2224
},
2325
"devDependencies": {
2426
"eslint": "1.10.3",

test/local.js

Lines changed: 128 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
var expect = require('expect.js'),
2+
sinon = require('sinon'),
23
mocks = require('mocks'),
34
path = require('path'),
45
fs = require('fs'),
56
rimraf = require('rimraf'),
67
Proxy = require('proxy'),
8+
tempfs = require('temp-fs'),
79
browserstack = require('../index'),
810
LocalBinary = require('../lib/LocalBinary');
911

12+
13+
const MAX_TIMEOUT = 600000;
14+
1015
describe('Local', function () {
1116
var bsLocal;
1217
beforeEach(function () {
@@ -157,53 +162,138 @@ describe('Local', function () {
157162
});
158163

159164
describe('LocalBinary', function () {
160-
var proxy;
161-
var proxyPort;
162-
var binary;
163-
var tempDownloadPath;
164-
165-
before(function (done) {
166-
// setup HTTP proxy server
167-
proxy = new Proxy();
168-
proxy.listen(function () {
169-
proxyPort = proxy.address().port;
170-
done();
165+
describe('Retries', function() {
166+
var unlinkTmp,
167+
defaultBinaryPath,
168+
validBinaryPath,
169+
sandBox;
170+
171+
before(function(done) {
172+
this.timeout(MAX_TIMEOUT);
173+
// ensure that we have a valid binary downloaded
174+
175+
// removeIfInvalid();
176+
(new LocalBinary()).binaryPath({}, function(binaryPath) {
177+
defaultBinaryPath = binaryPath;
178+
tempfs.mkdir({
179+
recursive: true
180+
}, function(err, dir) {
181+
if(err) { throw err; }
182+
183+
validBinaryPath = path.join(dir.path, path.basename(binaryPath));
184+
fs.rename(defaultBinaryPath, validBinaryPath, function(err) {
185+
if(err) { throw err; }
186+
187+
unlinkTmp = dir.unlink;
188+
done();
189+
});
190+
});
191+
});
171192
});
172-
});
173193

174-
after(function (done) {
175-
proxy.once('close', function () { done(); });
176-
proxy.close();
177-
});
194+
beforeEach(function() {
195+
sandBox = sinon.sandbox.create();
196+
});
178197

179-
beforeEach(function () {
180-
binary = new LocalBinary();
181-
tempDownloadPath = path.join(process.cwd(), 'download');
182-
});
198+
it('Tries to download binary if its corrupted', function(done) {
199+
fs.unlink(defaultBinaryPath, function() {
200+
var localBinary = new LocalBinary();
201+
var downloadStub = sandBox.stub(localBinary, 'download', function() {
202+
downloadStub.callArgWith(2, [ defaultBinaryPath ]);
203+
expect(downloadStub.args[0][3]).to.be(5);
204+
});
183205

184-
afterEach(function () {
185-
rimraf.sync(tempDownloadPath);
186-
});
206+
fs.writeFile(defaultBinaryPath, 'Random String', function() {
207+
fs.chmod(defaultBinaryPath, '0755', function() {
208+
localBinary.binaryPath({
209+
}, function(binaryPath) {
210+
expect(downloadStub.called).to.be.true;
211+
done();
212+
});
213+
});
214+
});
215+
});
216+
});
187217

188-
it('should download binaries without proxy', function (done) {
189-
this.timeout(600000);
190-
var conf = {};
191-
binary.download(conf, tempDownloadPath, function (result) {
192-
expect(fs.existsSync(result)).to.equal(true);
218+
it('Tries to download binary if its not present', function(done) {
219+
fs.unlink(defaultBinaryPath, function() {
220+
var localBinary = new LocalBinary();
221+
var downloadStub = sandBox.stub(localBinary, 'download', function() {
222+
downloadStub.callArgWith(2, [ defaultBinaryPath ]);
223+
expect(downloadStub.args[0][3]).to.be(5);
224+
});
225+
226+
localBinary.binaryPath({
227+
}, function(binaryPath) {
228+
expect(downloadStub.called).to.be.true;
229+
done();
230+
});
231+
});
232+
});
233+
234+
afterEach(function(done) {
235+
sandBox.restore();
193236
done();
194237
});
238+
239+
after(function(done) {
240+
fs.rename(validBinaryPath, defaultBinaryPath, function(err) {
241+
if(err) { throw err; }
242+
243+
unlinkTmp(done);
244+
});
245+
});
195246
});
196247

197-
it('should download binaries with proxy', function (done) {
198-
this.timeout(600000);
199-
var conf = {
200-
proxyHost: '127.0.0.1',
201-
proxyPort: proxyPort
202-
};
203-
binary.download(conf, tempDownloadPath, function (result) {
204-
// test for file existence
205-
expect(fs.existsSync(result)).to.equal(true);
206-
done();
248+
describe('Download', function() {
249+
var proxy;
250+
var proxyPort;
251+
var binary;
252+
var tempDownloadPath;
253+
254+
before(function (done) {
255+
// setup HTTP proxy server
256+
proxy = new Proxy();
257+
proxy.listen(function () {
258+
proxyPort = proxy.address().port;
259+
done();
260+
});
261+
});
262+
263+
after(function (done) {
264+
proxy.once('close', function () { done(); });
265+
proxy.close();
266+
});
267+
268+
beforeEach(function () {
269+
binary = new LocalBinary();
270+
tempDownloadPath = path.join(process.cwd(), 'download');
271+
});
272+
273+
afterEach(function () {
274+
rimraf.sync(tempDownloadPath);
275+
});
276+
277+
it('should download binaries without proxy', function (done) {
278+
this.timeout(MAX_TIMEOUT);
279+
var conf = {};
280+
binary.download(conf, tempDownloadPath, function (result) {
281+
expect(fs.existsSync(result)).to.equal(true);
282+
done();
283+
});
284+
});
285+
286+
it('should download binaries with proxy', function (done) {
287+
this.timeout(MAX_TIMEOUT);
288+
var conf = {
289+
proxyHost: '127.0.0.1',
290+
proxyPort: proxyPort
291+
};
292+
binary.download(conf, tempDownloadPath, function (result) {
293+
// test for file existence
294+
expect(fs.existsSync(result)).to.equal(true);
295+
done();
296+
});
207297
});
208298
});
209299
});

0 commit comments

Comments
 (0)