def test_scan_skipped_if_builderfactory_stale(self): # singleCycle does nothing if the BuilderFactory's update # timestamp is older than the end of the previous scan. This # prevents eg. a scan after a dispatch from failing to notice # that a build has been dispatched. pbf = PrefetchedBuilderFactory() pbf.update() scanner = self._getScanner(builder_factory=pbf) fake_scan = FakeMethod() def _fake_scan(): fake_scan() return defer.succeed(None) scanner.scan = _fake_scan self.assertEqual(0, fake_scan.call_count) # An initial cycle triggers a scan. yield scanner.singleCycle() self.assertEqual(1, fake_scan.call_count) # But a subsequent cycle without updating BuilderFactory's data # is a no-op. yield scanner.singleCycle() self.assertEqual(1, fake_scan.call_count) # Updating the BuilderFactory causes scans to resume. pbf.update() yield scanner.singleCycle() self.assertEqual(2, fake_scan.call_count)
def test_update(self): # update grabs all of the Builders and their BuildQueues in a # single query. builders = [self.factory.makeBuilder() for i in range(5)] for i in range(3): bq = self.factory.makeBinaryPackageBuild().queueBuild() bq.markAsBuilding(builders[i]) pbf = PrefetchedBuilderFactory() transaction.commit() pbf.update() with StormStatementRecorder() as recorder: pbf.update() self.assertThat(recorder, HasQueryCount(Equals(1)))
def test_iterVitals(self): # PrefetchedBuilderFactory.iterVitals looks up the details from # the local cached map, without hitting the DB. # Construct 5 new builders, 3 with builds. This is in addition # to the 2 in sampledata, 1 with a build. builders = [self.factory.makeBuilder() for i in range(5)] for i in range(3): bq = self.factory.makeBinaryPackageBuild().queueBuild() bq.markAsBuilding(builders[i]) transaction.commit() pbf = PrefetchedBuilderFactory() pbf.update() with StormStatementRecorder() as recorder: all_vitals = list(pbf.iterVitals()) self.assertThat(recorder, HasQueryCount(Equals(0))) # Compare the counts with what we expect, and the full result # with the non-prefetching BuilderFactory. self.assertEqual(7, len(all_vitals)) self.assertEqual( 4, len([v for v in all_vitals if v.build_queue is not None])) self.assertContentEqual(BuilderFactory().iterVitals(), all_vitals)
def test_getVitals(self): # PrefetchedBuilderFactory.getVitals looks up the BuilderVitals # in a local cached map, without hitting the DB. builder = self.factory.makeBuilder() bq = self.factory.makeBinaryPackageBuild().queueBuild() bq.markAsBuilding(builder) transaction.commit() name = builder.name pbf = PrefetchedBuilderFactory() pbf.update() def assertQuerylessVitals(comparator): expected_vitals = extract_vitals_from_db(builder) transaction.commit() with StormStatementRecorder() as recorder: got_vitals = pbf.getVitals(name) comparator(expected_vitals, got_vitals) comparator(expected_vitals.build_queue, got_vitals.build_queue) self.assertThat(recorder, HasQueryCount(Equals(0))) return got_vitals # We can get the vitals of a builder from the factory without # any DB queries. vitals = assertQuerylessVitals(self.assertEqual) self.assertIsNot(None, vitals.build_queue) # If we cancel the BuildQueue to unassign it, the factory # doesn't notice immediately. bq.cancel() vitals = assertQuerylessVitals(self.assertNotEqual) self.assertIsNot(None, vitals.build_queue) # But the vitals will show the builder as idle if we ask the # factory to refetch. pbf.update() vitals = assertQuerylessVitals(self.assertEqual) self.assertIs(None, vitals.build_queue)
def test_get(self): # PrefetchedBuilderFactory.__getitem__ is unoptimised, just # querying and returning the named builder. builder = self.factory.makeBuilder() pbf = PrefetchedBuilderFactory() self.assertEqual(builder, pbf[builder.name])