1 ToolTest = function(name, extraOptions) {
  2     this.name = name;
  3     this.options = extraOptions;
  4     this.port = allocatePort();
  5     this.baseName = "jstests_tool_" + name;
  6     this.root = MongoRunner.dataPath + this.baseName;
  7     this.dbpath = this.root + "/";
  8     this.ext = this.root + "_external/";
  9     this.extFile = this.root + "_external/a";
 10     resetDbpath(this.dbpath);
 11     resetDbpath(this.ext);
 12 };
 13 
 14 ToolTest.prototype.startDB = function(coll) {
 15     assert(!this.m, "db already running");
 16 
 17     var options = {
 18         port: this.port,
 19         dbpath: this.dbpath,
 20         nohttpinterface: "",
 21         noprealloc: "",
 22         smallfiles: "",
 23         bind_ip: "127.0.0.1"
 24     };
 25 
 26     Object.extend(options, this.options);
 27 
 28     this.m = startMongoProgram.apply(null, MongoRunner.arrOptions("mongod", options));
 29     this.db = this.m.getDB(this.baseName);
 30     if (coll)
 31         return this.db.getCollection(coll);
 32     return this.db;
 33 };
 34 
 35 ToolTest.prototype.stop = function() {
 36     if (!this.m)
 37         return;
 38     _stopMongoProgram(this.port);
 39     this.m = null;
 40     this.db = null;
 41 
 42     print('*** ' + this.name + " completed successfully ***");
 43 };
 44 
 45 ToolTest.prototype.runTool = function() {
 46     var a = ["mongo" + arguments[0]];
 47 
 48     var hasdbpath = false;
 49     var hasDialTimeout = false;
 50 
 51     for (var i = 1; i < arguments.length; i++) {
 52         a.push(arguments[i]);
 53         if (arguments[i] === "--dbpath")
 54             hasdbpath = true;
 55         if (arguments[i] === "--dialTimeout")
 56             hasDialTimeout = true;
 57     }
 58 
 59     if (!hasdbpath) {
 60         a.push("--host");
 61         a.push("127.0.0.1:" + this.port);
 62     }
 63 
 64     if (!hasDialTimeout) {
 65         a.push("--dialTimeout");
 66         a.push("30");
 67     }
 68 
 69     return runMongoProgram.apply(null, a);
 70 };
 71 
 72 ReplTest = function(name, ports) {
 73     this.name = name;
 74     this.ports = ports || allocatePorts(2);
 75 };
 76 
 77 ReplTest.prototype.getPort = function(master) {
 78     if (master)
 79         return this.ports[0];
 80     return this.ports[1];
 81 };
 82 
 83 ReplTest.prototype.getPath = function(master) {
 84     var p = MongoRunner.dataPath + this.name + "-";
 85     if (master)
 86         p += "master";
 87     else
 88         p += "slave";
 89     return p;
 90 };
 91 
 92 ReplTest.prototype.getOptions = function(master, extra, putBinaryFirst, norepl) {
 93 
 94     if (!extra)
 95         extra = {};
 96 
 97     if (!extra.oplogSize)
 98         extra.oplogSize = "40";
 99 
100     var a = [];
101     if (putBinaryFirst)
102         a.push("mongod");
103     a.push("--nohttpinterface", "--noprealloc", "--bind_ip", "127.0.0.1", "--smallfiles");
104 
105     a.push("--port");
106     a.push(this.getPort(master));
107 
108     a.push("--dbpath");
109     a.push(this.getPath(master));
110 
111     if (jsTestOptions().noJournal && !('journal' in extra))
112         a.push("--nojournal");
113     if (jsTestOptions().noJournalPrealloc)
114         a.push("--nopreallocj");
115     if (jsTestOptions().keyFile) {
116         a.push("--keyFile");
117         a.push(jsTestOptions().keyFile);
118     }
119 
120     if (!norepl) {
121         if (master) {
122             a.push("--master");
123         } else {
124             a.push("--slave");
125             a.push("--source");
126             a.push("127.0.0.1:" + this.ports[0]);
127         }
128     }
129 
130     for (var k in extra) {
131         var v = extra[k];
132         if (k in MongoRunner.logicalOptions)
133             continue;
134         a.push("--" + k);
135         if (v != null && v !== "")
136             a.push(v);
137     }
138 
139     return a;
140 };
141 
142 ReplTest.prototype.start = function(master, options, restart, norepl) {
143     var lockFile = this.getPath(master) + "/mongod.lock";
144     removeFile(lockFile);
145     var o = this.getOptions(master, options, restart, norepl);
146 
147     if (restart) {
148         var conn = startMongoProgram.apply(null, o);
149         if (!master) {
150             conn.setSlaveOk();
151         }
152         return conn;
153     } else {
154         var conn = _startMongod.apply(null, o);
155         if (jsTestOptions().keyFile || jsTestOptions().auth) {
156             jsTest.authenticate(conn);
157         }
158         if (!master) {
159             conn.setSlaveOk();
160         }
161         return conn;
162     }
163 };
164 
165 ReplTest.prototype.stop = function(master, signal) {
166     if (arguments.length == 0) {
167         this.stop(true);
168         this.stop(false);
169         return;
170     }
171 
172     print('*** ' + this.name + " completed successfully ***");
173     return _stopMongoProgram(this.getPort(master), signal || 15);
174 };
175 
176 /**
177  * Returns a port number that has not been given out to any other caller from the same mongo shell.
178  */
179 allocatePort = (function() {
180     // Defer initializing these variables until the first call, as TestData attributes may be
181     // initialized as part of the --eval argument (e.g. by resmoke.py), which will not be evaluated
182     // until after this has loaded.
183     var maxPort;
184     var nextPort;
185 
186     return function() {
187         // The default port was chosen in an attempt to have a large number of unassigned ports that
188         // are also outside the ephemeral port range.
189         nextPort = nextPort || jsTestOptions().minPort || 20000;
190         maxPort = maxPort || jsTestOptions().maxPort || Math.pow(2, 16) - 1;
191 
192         if (nextPort === maxPort) {
193             throw new Error("Exceeded maximum port range in allocatePort()");
194         }
195         return nextPort++;
196     };
197 })();
198 
199 /**
200  * Returns a list of 'numPorts' port numbers that have not been given out to any other caller from
201  * the same mongo shell.
202  */
203 allocatePorts = function(numPorts) {
204     var ports = [];
205     for (var i = 0; i < numPorts; i++) {
206         ports.push(allocatePort());
207     }
208 
209     return ports;
210 };
211 
212 function startParallelShell(jsCode, port, noConnect) {
213     var args = ["mongo"];
214 
215     if (typeof db == "object") {
216         var hostAndPort = db.getMongo().host.split(':');
217         var host = hostAndPort[0];
218         args.push("--host", host);
219         if (!port && hostAndPort.length >= 2) {
220             var port = hostAndPort[1];
221         }
222     }
223     if (port) {
224         args.push("--port", port);
225     }
226 
227     // Convert function into call-string
228     if (typeof(jsCode) == "function") {
229         jsCode = "(" + jsCode.toString() + ")();";
230     } else if (typeof(jsCode) == "string") {
231     }
232     // do nothing
233     else {
234         throw Error("bad first argument to startParallelShell");
235     }
236 
237     if (noConnect) {
238         args.push("--nodb");
239     } else if (typeof(db) == "object") {
240         jsCode = "db = db.getSiblingDB('" + db.getName() + "');" + jsCode;
241     }
242 
243     if (TestData) {
244         jsCode = "TestData = " + tojson(TestData) + ";" + jsCode;
245     }
246 
247     args.push("--eval", jsCode);
248 
249     var pid = startMongoProgramNoConnect.apply(null, args);
250 
251     // Returns a function that when called waits for the parallel shell to exit and returns the exit
252     // code of the process. By default an error is thrown if the parallel shell exits with a nonzero
253     // exit code.
254     return function(options) {
255         if (arguments.length > 0) {
256             if (typeof options !== "object") {
257                 throw new Error("options must be an object");
258             }
259             if (options === null) {
260                 throw new Error("options cannot be null");
261             }
262         }
263         var exitCode = waitProgram(pid);
264         if (arguments.length === 0 || options.checkExitSuccess) {
265             assert.eq(0, exitCode, "encountered an error in the parallel shell");
266         }
267         return exitCode;
268     };
269 }
270 
271 var testingReplication = false;
272