| OLD | NEW |
| (Empty) |
| 1 | |
| 2 # this file tests the 'buildbot' command, with its various sub-commands | |
| 3 | |
| 4 from twisted.trial import unittest | |
| 5 from twisted.python import usage | |
| 6 import os, shutil, shlex | |
| 7 import sets | |
| 8 | |
| 9 from buildbot.scripts import runner, tryclient | |
| 10 | |
| 11 class Options(unittest.TestCase): | |
| 12 optionsFile = "SDFsfsFSdfsfsFSD" | |
| 13 | |
| 14 def make(self, d, key): | |
| 15 # we use a wacky filename here in case the test code discovers the | |
| 16 # user's real ~/.buildbot/ directory | |
| 17 if not os.path.isdir(os.sep.join(d + [".buildbot"])): | |
| 18 os.makedirs(os.sep.join(d + [".buildbot"])) | |
| 19 f = open(os.sep.join(d + [".buildbot", self.optionsFile]), "w") | |
| 20 f.write("key = '%s'\n" % key) | |
| 21 f.close() | |
| 22 | |
| 23 def check(self, d, key): | |
| 24 basedir = os.sep.join(d) | |
| 25 options = runner.loadOptionsFile(self.optionsFile, here=basedir, | |
| 26 home=self.home) | |
| 27 if key is None: | |
| 28 self.failIf(options.has_key('key')) | |
| 29 else: | |
| 30 self.failUnlessEqual(options['key'], key) | |
| 31 | |
| 32 def testFindOptions(self): | |
| 33 self.make(["home", "dir1", "dir2", "dir3"], "one") | |
| 34 self.make(["home", "dir1", "dir2"], "two") | |
| 35 self.make(["home"], "home") | |
| 36 self.home = os.path.abspath("home") | |
| 37 | |
| 38 self.check(["home", "dir1", "dir2", "dir3"], "one") | |
| 39 self.check(["home", "dir1", "dir2"], "two") | |
| 40 self.check(["home", "dir1"], "home") | |
| 41 | |
| 42 self.home = os.path.abspath("nothome") | |
| 43 if not os.path.isdir(os.sep.join(["nothome", "dir1"])): | |
| 44 os.makedirs(os.sep.join(["nothome", "dir1"])) | |
| 45 self.check(["nothome", "dir1"], None) | |
| 46 | |
| 47 def doForce(self, args, expected): | |
| 48 o = runner.ForceOptions() | |
| 49 o.parseOptions(args) | |
| 50 self.failUnlessEqual(o.keys(), expected.keys()) | |
| 51 for k in o.keys(): | |
| 52 self.failUnlessEqual(o[k], expected[k], | |
| 53 "[%s] got %s instead of %s" % (k, o[k], | |
| 54 expected[k])) | |
| 55 | |
| 56 def testForceOptions(self): | |
| 57 if not hasattr(shlex, "split"): | |
| 58 raise unittest.SkipTest("need python>=2.3 for shlex.split") | |
| 59 | |
| 60 exp = {"builder": "b1", "reason": "reason", | |
| 61 "branch": None, "revision": None} | |
| 62 self.doForce(shlex.split("b1 reason"), exp) | |
| 63 self.doForce(shlex.split("b1 'reason'"), exp) | |
| 64 self.failUnlessRaises(usage.UsageError, self.doForce, | |
| 65 shlex.split("--builder b1 'reason'"), exp) | |
| 66 self.doForce(shlex.split("--builder b1 --reason reason"), exp) | |
| 67 self.doForce(shlex.split("--builder b1 --reason 'reason'"), exp) | |
| 68 self.doForce(shlex.split("--builder b1 --reason \"reason\""), exp) | |
| 69 | |
| 70 exp['reason'] = "longer reason" | |
| 71 self.doForce(shlex.split("b1 'longer reason'"), exp) | |
| 72 self.doForce(shlex.split("b1 longer reason"), exp) | |
| 73 self.doForce(shlex.split("--reason 'longer reason' b1"), exp) | |
| 74 | |
| 75 | |
| 76 class Create(unittest.TestCase): | |
| 77 def failUnlessIn(self, substring, string, msg=None): | |
| 78 # trial provides a version of this that requires python-2.3 to test | |
| 79 # strings. | |
| 80 self.failUnless(string.find(substring) != -1, msg) | |
| 81 def failUnlessExists(self, filename): | |
| 82 self.failUnless(os.path.exists(filename), "%s should exist" % filename) | |
| 83 def failIfExists(self, filename): | |
| 84 self.failIf(os.path.exists(filename), "%s should not exist" % filename) | |
| 85 | |
| 86 def setUp(self): | |
| 87 self.cwd = os.getcwd() | |
| 88 | |
| 89 def tearDown(self): | |
| 90 os.chdir(self.cwd) | |
| 91 | |
| 92 def testMaster(self): | |
| 93 basedir = "test_runner.master" | |
| 94 options = runner.MasterOptions() | |
| 95 options.parseOptions(["-q", basedir]) | |
| 96 cwd = os.getcwd() | |
| 97 runner.createMaster(options) | |
| 98 os.chdir(cwd) | |
| 99 | |
| 100 tac = os.path.join(basedir, "buildbot.tac") | |
| 101 self.failUnless(os.path.exists(tac)) | |
| 102 tacfile = open(tac,"rt").read() | |
| 103 self.failUnlessIn("basedir", tacfile) | |
| 104 self.failUnlessIn("configfile = r'master.cfg'", tacfile) | |
| 105 self.failUnlessIn("BuildMaster(basedir, configfile)", tacfile) | |
| 106 | |
| 107 cfg = os.path.join(basedir, "master.cfg") | |
| 108 self.failIfExists(cfg) | |
| 109 samplecfg = os.path.join(basedir, "master.cfg.sample") | |
| 110 self.failUnlessExists(samplecfg) | |
| 111 cfgfile = open(samplecfg,"rt").read() | |
| 112 self.failUnlessIn("This is a sample buildmaster config file", cfgfile) | |
| 113 | |
| 114 makefile = os.path.join(basedir, "Makefile.sample") | |
| 115 self.failUnlessExists(makefile) | |
| 116 | |
| 117 # now verify that running it a second time (with the same options) | |
| 118 # does the right thing: nothing changes | |
| 119 runner.createMaster(options) | |
| 120 os.chdir(cwd) | |
| 121 | |
| 122 self.failIfExists(os.path.join(basedir, "buildbot.tac.new")) | |
| 123 self.failUnlessExists(os.path.join(basedir, "master.cfg.sample")) | |
| 124 | |
| 125 oldtac = open(os.path.join(basedir, "buildbot.tac"), "rt").read() | |
| 126 | |
| 127 # mutate Makefile.sample, since it should be rewritten | |
| 128 f = open(os.path.join(basedir, "Makefile.sample"), "rt") | |
| 129 oldmake = f.read() | |
| 130 f = open(os.path.join(basedir, "Makefile.sample"), "wt") | |
| 131 f.write(oldmake) | |
| 132 f.write("# additional line added\n") | |
| 133 f.close() | |
| 134 | |
| 135 # also mutate master.cfg.sample | |
| 136 f = open(os.path.join(basedir, "master.cfg.sample"), "rt") | |
| 137 oldsamplecfg = f.read() | |
| 138 f = open(os.path.join(basedir, "master.cfg.sample"), "wt") | |
| 139 f.write(oldsamplecfg) | |
| 140 f.write("# additional line added\n") | |
| 141 f.close() | |
| 142 | |
| 143 # now run it again (with different options) | |
| 144 options = runner.MasterOptions() | |
| 145 options.parseOptions(["-q", "--config", "other.cfg", basedir]) | |
| 146 runner.createMaster(options) | |
| 147 os.chdir(cwd) | |
| 148 | |
| 149 tac = open(os.path.join(basedir, "buildbot.tac"), "rt").read() | |
| 150 self.failUnlessEqual(tac, oldtac, "shouldn't change existing .tac") | |
| 151 self.failUnlessExists(os.path.join(basedir, "buildbot.tac.new")) | |
| 152 | |
| 153 make = open(os.path.join(basedir, "Makefile.sample"), "rt").read() | |
| 154 self.failUnlessEqual(make, oldmake, "*should* rewrite Makefile.sample") | |
| 155 | |
| 156 samplecfg = open(os.path.join(basedir, "master.cfg.sample"), | |
| 157 "rt").read() | |
| 158 self.failUnlessEqual(samplecfg, oldsamplecfg, | |
| 159 "*should* rewrite master.cfg.sample") | |
| 160 | |
| 161 def testUpgradeMaster(self): | |
| 162 # first, create a master, run it briefly, then upgrade it. Nothing | |
| 163 # should change. | |
| 164 basedir = "test_runner.master2" | |
| 165 options = runner.MasterOptions() | |
| 166 options.parseOptions(["-q", basedir]) | |
| 167 cwd = os.getcwd() | |
| 168 runner.createMaster(options) | |
| 169 os.chdir(cwd) | |
| 170 | |
| 171 f = open(os.path.join(basedir, "master.cfg"), "w") | |
| 172 f.write(open(os.path.join(basedir, "master.cfg.sample"), "r").read()) | |
| 173 f.close() | |
| 174 | |
| 175 # the upgrade process (specifically the verify-master.cfg step) will | |
| 176 # create any builder status directories that weren't already created. | |
| 177 # Create those ahead of time. | |
| 178 os.mkdir(os.path.join(basedir, "full")) | |
| 179 | |
| 180 files1 = self.record_files(basedir) | |
| 181 | |
| 182 # upgrade it | |
| 183 options = runner.UpgradeMasterOptions() | |
| 184 options.parseOptions(["--quiet", basedir]) | |
| 185 cwd = os.getcwd() | |
| 186 runner.upgradeMaster(options) | |
| 187 os.chdir(cwd) | |
| 188 | |
| 189 files2 = self.record_files(basedir) | |
| 190 self.failUnlessSameFiles(files1, files2) | |
| 191 | |
| 192 # now make it look like the one that 0.7.5 creates: no public_html | |
| 193 for fn in os.listdir(os.path.join(basedir, "public_html")): | |
| 194 os.unlink(os.path.join(basedir, "public_html", fn)) | |
| 195 os.rmdir(os.path.join(basedir, "public_html")) | |
| 196 | |
| 197 # and make sure that upgrading it re-populates public_html | |
| 198 options = runner.UpgradeMasterOptions() | |
| 199 options.parseOptions(["-q", basedir]) | |
| 200 cwd = os.getcwd() | |
| 201 runner.upgradeMaster(options) | |
| 202 os.chdir(cwd) | |
| 203 | |
| 204 files3 = self.record_files(basedir) | |
| 205 self.failUnlessSameFiles(files1, files3) | |
| 206 | |
| 207 # now induce an error in master.cfg and make sure that upgrade | |
| 208 # notices it. | |
| 209 f = open(os.path.join(basedir, "master.cfg"), "a") | |
| 210 f.write("raise RuntimeError('catch me please')\n") | |
| 211 f.close() | |
| 212 | |
| 213 options = runner.UpgradeMasterOptions() | |
| 214 options.parseOptions(["-q", basedir]) | |
| 215 cwd = os.getcwd() | |
| 216 rc = runner.upgradeMaster(options) | |
| 217 os.chdir(cwd) | |
| 218 self.failUnless(rc != 0, rc) | |
| 219 # TODO: change the way runner.py works to let us pass in a stderr | |
| 220 # filehandle, and use a StringIO to capture its output, and make sure | |
| 221 # the right error messages appear therein. | |
| 222 | |
| 223 | |
| 224 def failUnlessSameFiles(self, files1, files2): | |
| 225 f1 = sets.Set(files1.keys()) | |
| 226 f2 = sets.Set(files2.keys()) | |
| 227 msg = "" | |
| 228 if f2 - f1: | |
| 229 msg += "Missing from files1: %s\n" % (list(f2-f1),) | |
| 230 if f1 - f2: | |
| 231 msg += "Missing from files2: %s\n" % (list(f1-f2),) | |
| 232 if msg: | |
| 233 self.fail(msg) | |
| 234 | |
| 235 def record_files(self, basedir): | |
| 236 allfiles = {} | |
| 237 for root, dirs, files in os.walk(basedir): | |
| 238 for f in files: | |
| 239 fn = os.path.join(root, f) | |
| 240 allfiles[fn] = ("FILE", open(fn,"rb").read()) | |
| 241 for d in dirs: | |
| 242 allfiles[os.path.join(root, d)] = ("DIR",) | |
| 243 return allfiles | |
| 244 | |
| 245 | |
| 246 def testSlave(self): | |
| 247 basedir = "test_runner.slave" | |
| 248 options = runner.SlaveOptions() | |
| 249 options.parseOptions(["-q", basedir, "buildmaster:1234", | |
| 250 "botname", "passwd"]) | |
| 251 cwd = os.getcwd() | |
| 252 runner.createSlave(options) | |
| 253 os.chdir(cwd) | |
| 254 | |
| 255 tac = os.path.join(basedir, "buildbot.tac") | |
| 256 self.failUnless(os.path.exists(tac)) | |
| 257 tacfile = open(tac,"rt").read() | |
| 258 self.failUnlessIn("basedir", tacfile) | |
| 259 self.failUnlessIn("buildmaster_host = 'buildmaster'", tacfile) | |
| 260 self.failUnlessIn("port = 1234", tacfile) | |
| 261 self.failUnlessIn("slavename = 'botname'", tacfile) | |
| 262 self.failUnlessIn("passwd = 'passwd'", tacfile) | |
| 263 self.failUnlessIn("keepalive = 600", tacfile) | |
| 264 self.failUnlessIn("BuildSlave(buildmaster_host, port, slavename", | |
| 265 tacfile) | |
| 266 | |
| 267 makefile = os.path.join(basedir, "Makefile.sample") | |
| 268 self.failUnlessExists(makefile) | |
| 269 | |
| 270 self.failUnlessExists(os.path.join(basedir, "info", "admin")) | |
| 271 self.failUnlessExists(os.path.join(basedir, "info", "host")) | |
| 272 # edit one to make sure the later install doesn't change it | |
| 273 f = open(os.path.join(basedir, "info", "admin"), "wt") | |
| 274 f.write("updated@buildbot.example.org\n") | |
| 275 f.close() | |
| 276 | |
| 277 # now verify that running it a second time (with the same options) | |
| 278 # does the right thing: nothing changes | |
| 279 runner.createSlave(options) | |
| 280 os.chdir(cwd) | |
| 281 | |
| 282 self.failIfExists(os.path.join(basedir, "buildbot.tac.new")) | |
| 283 admin = open(os.path.join(basedir, "info", "admin"), "rt").read() | |
| 284 self.failUnlessEqual(admin, "updated@buildbot.example.org\n") | |
| 285 | |
| 286 | |
| 287 # mutate Makefile.sample, since it should be rewritten | |
| 288 oldmake = open(os.path.join(basedir, "Makefile.sample"), "rt").read() | |
| 289 f = open(os.path.join(basedir, "Makefile.sample"), "wt") | |
| 290 f.write(oldmake) | |
| 291 f.write("# additional line added\n") | |
| 292 f.close() | |
| 293 oldtac = open(os.path.join(basedir, "buildbot.tac"), "rt").read() | |
| 294 | |
| 295 # now run it again (with different options) | |
| 296 options = runner.SlaveOptions() | |
| 297 options.parseOptions(["-q", "--keepalive", "30", | |
| 298 basedir, "buildmaster:9999", | |
| 299 "newbotname", "passwd"]) | |
| 300 runner.createSlave(options) | |
| 301 os.chdir(cwd) | |
| 302 | |
| 303 tac = open(os.path.join(basedir, "buildbot.tac"), "rt").read() | |
| 304 self.failUnlessEqual(tac, oldtac, "shouldn't change existing .tac") | |
| 305 self.failUnlessExists(os.path.join(basedir, "buildbot.tac.new")) | |
| 306 tacfile = open(os.path.join(basedir, "buildbot.tac.new"),"rt").read() | |
| 307 self.failUnlessIn("basedir", tacfile) | |
| 308 self.failUnlessIn("buildmaster_host = 'buildmaster'", tacfile) | |
| 309 self.failUnlessIn("port = 9999", tacfile) | |
| 310 self.failUnlessIn("slavename = 'newbotname'", tacfile) | |
| 311 self.failUnlessIn("passwd = 'passwd'", tacfile) | |
| 312 self.failUnlessIn("keepalive = 30", tacfile) | |
| 313 self.failUnlessIn("BuildSlave(buildmaster_host, port, slavename", | |
| 314 tacfile) | |
| 315 | |
| 316 make = open(os.path.join(basedir, "Makefile.sample"), "rt").read() | |
| 317 self.failUnlessEqual(make, oldmake, "*should* rewrite Makefile.sample") | |
| 318 | |
| 319 class Try(unittest.TestCase): | |
| 320 # test some aspects of the 'buildbot try' command | |
| 321 def makeOptions(self, contents): | |
| 322 if os.path.exists(".buildbot"): | |
| 323 shutil.rmtree(".buildbot") | |
| 324 os.mkdir(".buildbot") | |
| 325 open(os.path.join(".buildbot", "options"), "w").write(contents) | |
| 326 | |
| 327 def testGetopt1(self): | |
| 328 opts = "try_connect = 'ssh'\n" + "try_builders = ['a']\n" | |
| 329 self.makeOptions(opts) | |
| 330 config = runner.TryOptions() | |
| 331 config.parseOptions([]) | |
| 332 t = tryclient.Try(config) | |
| 333 self.failUnlessEqual(t.connect, "ssh") | |
| 334 self.failUnlessEqual(t.builderNames, ['a']) | |
| 335 | |
| 336 def testGetopt2(self): | |
| 337 opts = "" | |
| 338 self.makeOptions(opts) | |
| 339 config = runner.TryOptions() | |
| 340 config.parseOptions(['--connect=ssh', '--builder', 'a']) | |
| 341 t = tryclient.Try(config) | |
| 342 self.failUnlessEqual(t.connect, "ssh") | |
| 343 self.failUnlessEqual(t.builderNames, ['a']) | |
| 344 | |
| 345 def testGetopt3(self): | |
| 346 opts = "" | |
| 347 self.makeOptions(opts) | |
| 348 config = runner.TryOptions() | |
| 349 config.parseOptions(['--connect=ssh', | |
| 350 '--builder', 'a', '--builder=b']) | |
| 351 t = tryclient.Try(config) | |
| 352 self.failUnlessEqual(t.connect, "ssh") | |
| 353 self.failUnlessEqual(t.builderNames, ['a', 'b']) | |
| 354 | |
| 355 def testGetopt4(self): | |
| 356 opts = "try_connect = 'ssh'\n" + "try_builders = ['a']\n" | |
| 357 self.makeOptions(opts) | |
| 358 config = runner.TryOptions() | |
| 359 config.parseOptions(['--builder=b']) | |
| 360 t = tryclient.Try(config) | |
| 361 self.failUnlessEqual(t.connect, "ssh") | |
| 362 self.failUnlessEqual(t.builderNames, ['b']) | |
| 363 | |
| 364 def testGetTopdir(self): | |
| 365 os.mkdir("gettopdir") | |
| 366 os.mkdir(os.path.join("gettopdir", "foo")) | |
| 367 os.mkdir(os.path.join("gettopdir", "foo", "bar")) | |
| 368 open(os.path.join("gettopdir", "1"),"w").write("1") | |
| 369 open(os.path.join("gettopdir", "foo", "2"),"w").write("2") | |
| 370 open(os.path.join("gettopdir", "foo", "bar", "3"),"w").write("3") | |
| 371 | |
| 372 target = os.path.abspath("gettopdir") | |
| 373 t = tryclient.getTopdir("1", "gettopdir") | |
| 374 self.failUnlessEqual(os.path.abspath(t), target) | |
| 375 t = tryclient.getTopdir("1", os.path.join("gettopdir", "foo")) | |
| 376 self.failUnlessEqual(os.path.abspath(t), target) | |
| 377 t = tryclient.getTopdir("1", os.path.join("gettopdir", "foo", "bar")) | |
| 378 self.failUnlessEqual(os.path.abspath(t), target) | |
| 379 | |
| 380 target = os.path.abspath(os.path.join("gettopdir", "foo")) | |
| 381 t = tryclient.getTopdir("2", os.path.join("gettopdir", "foo")) | |
| 382 self.failUnlessEqual(os.path.abspath(t), target) | |
| 383 t = tryclient.getTopdir("2", os.path.join("gettopdir", "foo", "bar")) | |
| 384 self.failUnlessEqual(os.path.abspath(t), target) | |
| 385 | |
| 386 target = os.path.abspath(os.path.join("gettopdir", "foo", "bar")) | |
| 387 t = tryclient.getTopdir("3", os.path.join("gettopdir", "foo", "bar")) | |
| 388 self.failUnlessEqual(os.path.abspath(t), target) | |
| 389 | |
| 390 nonexistent = "nonexistent\n29fis3kq\tBAR" | |
| 391 # hopefully there won't be a real file with that name between here | |
| 392 # and the filesystem root. | |
| 393 self.failUnlessRaises(ValueError, tryclient.getTopdir, nonexistent) | |
| 394 | |
| OLD | NEW |