class TestBravoFactoryPacks(unittest.TestCase): """ The plugin pack system should work. """ def test_pack_beta(self): """ The "beta" plugin pack should always work. Period. """ self.d = tempfile.mkdtemp() self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") d = { "authenticator": "offline", "mode": "creative", "packs": "beta", "port": "0", "serializer": "alpha", "url": "file://%s" % self.d, } for k, v in d.items(): self.bcp.set("world unittest", k, v) self.f = BravoFactory(self.bcp, self.name) # And now start the factory. self.f.startFactory() def tearDown(self): self.f.stopFactory() shutil.rmtree(self.d)
class TestWorld(unittest.TestCase): def setUp(self): self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "url", "") self.bcp.set("world unittest", "serializer", "memory") self.w = World(self.bcp, self.name) self.w.pipeline = [] self.w.start() def tearDown(self): self.w.stop() def test_trivial(self): pass def test_load_player_initial(self): """ Calling load_player() on a player which has never been loaded should not result in an exception. Instead, the player should be returned, wrapped in a Deferred. """ # For bonus points, assert that the player's username is correct. d = self.w.load_player("unittest") @d.addCallback def cb(player): self.assertEqual(player.username, "unittest") return d
class TestBravoFactoryPacks(unittest.TestCase): """ The plugin pack system should work. """ def test_pack_beta(self): """ The "beta" plugin pack should always work. Period. """ self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") d = { "mode": "creative", "packs": "beta", "port": "0", "serializer": "memory", "url": "", } for k, v in d.items(): self.bcp.set("world unittest", k, v) self.f = BravoFactory(self.bcp, self.name) # And now start the factory. self.f.startFactory() # And stop it, too. self.f.stopFactory()
class TestBravoFactoryPacks(unittest.TestCase): """ The plugin pack system should work. """ def test_pack_beta(self): """ The "beta" plugin pack should always work. Period. """ self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") d = { "mode" : "creative", "packs" : "beta", "port" : "0", "serializer" : "memory", "url" : "", } for k, v in d.items(): self.bcp.set("world unittest", k, v) self.f = BravoFactory(self.bcp, self.name) # And now start the factory. self.f.startFactory() # And stop it, too. self.f.stopFactory()
class TestWorldConfig(unittest.TestCase): def setUp(self): self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "url", "") self.bcp.set("world unittest", "serializer", "memory") self.w = World(self.bcp, self.name) self.w.pipeline = [] def test_trivial(self): pass def test_world_configured_seed(self): """ Worlds can have their seed set via configuration. """ self.bcp.set("world unittest", "seed", "42") self.w.start() self.assertEqual(self.w.level.seed, 42) self.w.stop()
class TestBravoConfigParser(unittest.TestCase): def setUp(self): self.bcp = BravoConfigParser() self.bcp.add_section("unittest") def test_trivial(self): pass def test_getlist(self): self.bcp.set("unittest", "l", "a,b,c,d") self.assertEqual(self.bcp.getlist("unittest", "l"), ["a", "b", "c", "d"]) def test_getlist_separator(self): self.bcp.set("unittest", "l", "a:b:c:d") self.assertEqual(self.bcp.getlist("unittest", "l", ":"), ["a", "b", "c", "d"]) def test_getlist_empty(self): self.bcp.set("unittest", "l", "") self.assertEqual(self.bcp.getlist("unittest", "l"), []) def test_getlist_whitespace(self): self.bcp.set("unittest", "l", " ") self.assertEqual(self.bcp.getlist("unittest", "l"), []) def test_getdefault(self): self.assertEqual(self.bcp.getdefault("unittest", "fake", ""), "") def test_getdefault_no_section(self): self.assertEqual(self.bcp.getdefault("fake", "fake", ""), "") def test_getbooleandefault(self): self.assertEqual(self.bcp.getbooleandefault("unittest", "fake", True), True) def test_getintdefault(self): self.assertEqual(self.bcp.getintdefault("unittest", "fake", 42), 42) def test_getlistdefault(self): self.assertEqual(self.bcp.getlistdefault("unittest", "fake", []), [])
class TestWorldChunks(unittest.TestCase): def setUp(self): self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "url", "") self.bcp.set("world unittest", "serializer", "memory") self.w = World(self.bcp, self.name) self.w.pipeline = [] self.w.start() def tearDown(self): self.w.stop() def test_trivial(self): pass @inlineCallbacks def test_request_chunk_identity(self): first = yield self.w.request_chunk(0, 0) second = yield self.w.request_chunk(0, 0) self.assertIs(first, second) @inlineCallbacks def test_request_chunk_cached_identity(self): # Turn on the cache and get a few chunks in there, then request a # chunk that is in the cache. yield self.w.enable_cache(1) first = yield self.w.request_chunk(0, 0) second = yield self.w.request_chunk(0, 0) self.assertIs(first, second) @inlineCallbacks def test_get_block(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.blocks = array("B") chunk.blocks.fromstring(os.urandom(32768)) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. block = yield self.w.get_block((x, y, z)) self.assertEqual(block, chunk.get_block((x, y, z))) @inlineCallbacks def test_get_metadata(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.metadata = array("B") chunk.metadata.fromstring(os.urandom(32768)) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. metadata = yield self.w.get_metadata((x, y, z)) self.assertEqual(metadata, chunk.get_metadata((x, y, z))) @inlineCallbacks def test_get_block_readback(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.blocks = array("B") chunk.blocks.fromstring(os.urandom(32768)) # Evict the chunk and grab it again. yield self.w.save_chunk(chunk) del chunk chunk = yield self.w.request_chunk(0, 0) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. block = yield self.w.get_block((x, y, z)) self.assertEqual(block, chunk.get_block((x, y, z))) @inlineCallbacks def test_get_block_readback_negative(self): chunk = yield self.w.request_chunk(-1, -1) # Fill the chunk with random stuff. chunk.blocks = array("B") chunk.blocks.fromstring(os.urandom(32768)) # Evict the chunk and grab it again. yield self.w.save_chunk(chunk) del chunk chunk = yield self.w.request_chunk(-1, -1) for x, y, z in product(xrange(2), repeat=3): block = yield self.w.get_block((x - 16, y, z - 16)) self.assertEqual(block, chunk.get_block((x, y, z))) @inlineCallbacks def test_get_metadata_readback(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.metadata = array("B") chunk.metadata.fromstring(os.urandom(32768)) # Evict the chunk and grab it again. yield self.w.save_chunk(chunk) del chunk chunk = yield self.w.request_chunk(0, 0) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. metadata = yield self.w.get_metadata((x, y, z)) self.assertEqual(metadata, chunk.get_metadata((x, y, z))) @inlineCallbacks def test_world_level_mark_chunk_dirty(self): chunk = yield self.w.request_chunk(0, 0) # Reload chunk. yield self.w.save_chunk(chunk) del chunk chunk = yield self.w.request_chunk(0, 0) self.assertFalse(chunk.dirty) self.w.mark_dirty((12, 64, 4)) chunk = yield self.w.request_chunk(0, 0) self.assertTrue(chunk.dirty) @inlineCallbacks def test_world_level_mark_chunk_dirty_offset(self): chunk = yield self.w.request_chunk(1, 2) # Reload chunk. yield self.w.save_chunk(chunk) del chunk chunk = yield self.w.request_chunk(1, 2) self.assertFalse(chunk.dirty) self.w.mark_dirty((29, 64, 43)) chunk = yield self.w.request_chunk(1, 2) self.assertTrue(chunk.dirty) @inlineCallbacks def test_sync_get_block(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.blocks = array("B") chunk.blocks.fromstring(os.urandom(32768)) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. block = self.w.sync_get_block((x, y, z)) self.assertEqual(block, chunk.get_block((x, y, z))) def test_sync_get_block_unloaded(self): self.assertRaises(ChunkNotLoaded, self.w.sync_get_block, (0, 0, 0)) def test_sync_get_metadata_neighboring(self): """ Even if a neighboring chunk is loaded, the target chunk could still be unloaded. Test with sync_get_metadata() to increase test coverage. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): self.assertRaises(ChunkNotLoaded, self.w.sync_get_metadata, (16, 0, 0)) return d
class TestGrass(TestCase): def setUp(self): self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "url", "") self.bcp.set("world unittest", "serializer", "memory") self.w = World(self.bcp, "unittest") self.w.pipeline = [] self.w.start() self.f = GrassMockFactory() self.f.world = self.w self.w.factory = self.f plugins = retrieve_plugins(IAutomaton, factory=self.f) self.hook = plugins["grass"] def tearDown(self): self.w.stop() def test_trivial(self): pass @inlineCallbacks def test_not_dirt(self): """ Blocks which aren't dirt by the time they're processed will be ignored. """ chunk = yield self.w.request_chunk(0, 0) chunk.set_block((0, 0, 0), blocks["bedrock"].slot) # Run the loop once. self.hook.feed((0, 0, 0)) self.hook.process() # We shouldn't have any pending blocks now. self.assertFalse(self.hook.tracked) @inlineCallbacks def test_unloaded_chunk(self): """ The grass automaton can't load chunks, so it will stop tracking blocks on the edge of the loaded world. """ chunk = yield self.w.request_chunk(0, 0) chunk.set_block((0, 0, 0), blocks["dirt"].slot) # Run the loop once. self.hook.feed((0, 0, 0)) self.hook.process() # We shouldn't have any pending blocks now. self.assertFalse(self.hook.tracked) @inlineCallbacks def test_surrounding(self): """ When surrounded by eight grassy neighbors, dirt should turn into grass immediately. """ chunk = yield self.w.request_chunk(0, 0) # Set up grassy surroundings. for x, z in product(xrange(0, 3), repeat=2): chunk.set_block((x, 0, z), blocks["grass"].slot) # Our lone Cinderella. chunk.set_block((1, 0, 1), blocks["dirt"].slot) # Do the actual hook run. This should take exactly one run. self.hook.feed((1, 0, 1)) self.hook.process() self.assertFalse(self.hook.tracked) self.assertEqual(chunk.get_block((1, 0, 1)), blocks["grass"].slot) def test_surrounding_not_dirt(self): """ Blocks which aren't dirt by the time they're processed will be ignored, even when surrounded by grass. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): # Set up grassy surroundings. for x, z in product(xrange(0, 3), repeat=2): chunk.set_block((x, 0, z), blocks["grass"].slot) chunk.set_block((1, 0, 1), blocks["bedrock"].slot) # Run the loop once. self.hook.feed((1, 0, 1)) self.hook.process() # We shouldn't have any pending blocks now. self.assertFalse(self.hook.tracked) return d @inlineCallbacks def test_surrounding_obstructed(self): """ Grass can't grow on blocks which have other blocks on top of them. """ chunk = yield self.w.request_chunk(0, 0) # Set up grassy surroundings. for x, z in product(xrange(0, 3), repeat=2): chunk.set_block((x, 0, z), blocks["grass"].slot) # Put an obstruction on top. chunk.set_block((1, 1, 1), blocks["stone"].slot) # Our lone Cinderella. chunk.set_block((1, 0, 1), blocks["dirt"].slot) # Do the actual hook run. This should take exactly one run. self.hook.feed((1, 0, 1)) self.hook.process() self.assertFalse(self.hook.tracked) self.assertEqual(chunk.get_block((1, 0, 1)), blocks["dirt"].slot) @inlineCallbacks def test_above(self): """ Grass spreads downwards. """ chunk = yield self.w.request_chunk(0, 0) # Set up grassy surroundings. for x, z in product(xrange(0, 3), repeat=2): chunk.set_block((x, 1, z), blocks["grass"].slot) chunk.destroy((1, 1, 1)) # Our lone Cinderella. chunk.set_block((1, 0, 1), blocks["dirt"].slot) # Do the actual hook run. This should take exactly one run. self.hook.feed((1, 0, 1)) self.hook.process() self.assertFalse(self.hook.tracked) self.assertEqual(chunk.get_block((1, 0, 1)), blocks["grass"].slot) def test_two_of_four(self): """ Grass should eventually spread to all filled-in plots on a 2x2 grid. Discovered by TkTech. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): for x, y, z in product(xrange(0, 4), xrange(0, 2), xrange(0, 4)): chunk.set_block((x, y, z), blocks["grass"].slot) for x, z in product(xrange(1, 3), repeat=2): chunk.set_block((x, 1, z), blocks["dirt"].slot) self.hook.feed((1, 1, 1)) self.hook.feed((2, 1, 1)) self.hook.feed((1, 1, 2)) self.hook.feed((2, 1, 2)) # Run to completion. This can take varying amounts of time # depending on the RNG, but it should be fairly speedy. # XXX patch the RNG so we can do this deterministically while self.hook.tracked: self.hook.process() self.assertEqual(chunk.get_block((1, 1, 1)), blocks["grass"].slot) self.assertEqual(chunk.get_block((2, 1, 1)), blocks["grass"].slot) self.assertEqual(chunk.get_block((1, 1, 2)), blocks["grass"].slot) self.assertEqual(chunk.get_block((2, 1, 2)), blocks["grass"].slot)
class TestRedstone(TestCase): def setUp(self): # Set up world. self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "url", "") self.bcp.set("world unittest", "serializer", "memory") self.w = World(self.bcp, self.name) self.w.pipeline = [] self.w.start() # And finally the mock factory. self.f = RedstoneMockFactory() self.f.world = self.w self.p = retrieve_plugins(IDigHook, factory=self.f) self.hook = self.p["redstone"] def tearDown(self): self.w.stop() def test_trivial(self): pass def test_and_gate(self): """ AND gates should work. This test also bumps up against a chunk boundary intentionally. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): for i1, i2, o in ( (False, False, False), (True, False, False), (False, True, False), (True, True, True), ): # Reset the hook. self.hook.asic = Asic() # The tableau. chunk.set_block((1, 1, 1), blocks["sand"].slot) chunk.set_block((1, 1, 2), blocks["sand"].slot) chunk.set_block((1, 1, 3), blocks["sand"].slot) chunk.set_block((1, 2, 1), blocks["redstone-torch"].slot) chunk.set_metadata((1, 2, 1), blocks["redstone-torch"].orientation("+y")) chunk.set_block((1, 2, 3), blocks["redstone-torch"].slot) chunk.set_metadata((1, 2, 3), blocks["redstone-torch"].orientation("+y")) chunk.set_block((1, 2, 2), blocks["redstone-wire"].slot) # Output torch. chunk.set_block((2, 1, 2), blocks["redstone-torch"].slot) chunk.set_metadata((2, 1, 2), blocks["redstone-torch"].orientation("+x")) # Attach the levers to the sand block. orientation = blocks["lever"].orientation("-x") iblock, imetadata = truthify_block(i1, blocks["lever"].slot, orientation) chunk.set_block((0, 1, 1), iblock) chunk.set_metadata((0, 1, 1), imetadata) iblock, imetadata = truthify_block(i2, blocks["lever"].slot, orientation) chunk.set_block((0, 1, 3), iblock) chunk.set_metadata((0, 1, 3), imetadata) # Run the circuit, starting at the switches. Six times: # Lever (x2), sand (x2), torch (x2), wire, block, torch. self.hook.feed((0, 1, 1)) self.hook.feed((0, 1, 3)) self.hook.process() self.hook.process() self.hook.process() self.hook.process() self.hook.process() self.hook.process() block = chunk.get_block((2, 1, 2)) metadata = chunk.get_metadata((2, 1, 2)) self.assertEqual((block, metadata), truthify_block(o, block, metadata)) return d def test_or_gate(self): """ OR gates should work. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): for i1, i2, o in ( (False, False, False), (True, False, True), (False, True, True), (True, True, True), ): # Reset the hook. self.hook.asic = Asic() # The tableau. chunk.set_block((1, 1, 2), blocks["sand"].slot) chunk.set_block((1, 2, 2), blocks["redstone-torch"].slot) chunk.set_metadata((1, 2, 2), blocks["redstone-torch"].orientation("+y")) chunk.set_block((2, 2, 2), blocks["redstone-wire"].slot) chunk.set_block((2, 1, 2), blocks["sand"].slot) chunk.set_block((3, 1, 2), blocks["redstone-torch"].slot) chunk.set_metadata((3, 1, 2), blocks["redstone-torch"].orientation("+x")) # Attach the levers to the sand block. orientation = blocks["lever"].orientation("-z") iblock, imetadata = truthify_block(i1, blocks["lever"].slot, orientation) chunk.set_block((1, 1, 1), iblock) chunk.set_metadata((1, 1, 1), imetadata) orientation = blocks["lever"].orientation("+z") iblock, imetadata = truthify_block(i2, blocks["lever"].slot, orientation) chunk.set_block((1, 1, 3), iblock) chunk.set_metadata((1, 1, 3), imetadata) # Run the circuit, starting at the switches. Six times: # Lever (x2), sand, torch, wire, sand, torch. self.hook.feed((1, 1, 1)) self.hook.feed((1, 1, 3)) self.hook.process() self.hook.process() self.hook.process() self.hook.process() self.hook.process() self.hook.process() block = chunk.get_block((3, 1, 2)) metadata = chunk.get_metadata((3, 1, 2)) self.assertEqual((block, metadata), truthify_block(o, block, metadata)) return d def test_nor_gate(self): """ NOR gates should work. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): for i1, i2, o in ( (False, False, True), (True, False, False), (False, True, False), (True, True, False), ): # Reset the hook. self.hook.asic = Asic() # The tableau. chunk.set_block((1, 1, 2), blocks["sand"].slot) chunk.set_block((2, 1, 2), blocks["redstone-torch"].slot) # Attach the levers to the sand block. orientation = blocks["lever"].orientation("-z") iblock, imetadata = truthify_block(i1, blocks["lever"].slot, orientation) chunk.set_block((1, 1, 1), iblock) chunk.set_metadata((1, 1, 1), imetadata) orientation = blocks["lever"].orientation("+z") iblock, imetadata = truthify_block(i2, blocks["lever"].slot, orientation) chunk.set_block((1, 1, 3), iblock) chunk.set_metadata((1, 1, 3), imetadata) # Attach the torch to the sand block too. orientation = blocks["redstone-torch"].orientation("+x") chunk.set_metadata((2, 1, 2), orientation) # Run the circuit, starting at the switches. Three times: # Lever (x2), sand, torch. self.hook.feed((1, 1, 1)) self.hook.feed((1, 1, 3)) self.hook.process() self.hook.process() self.hook.process() block = chunk.get_block((2, 1, 2)) metadata = chunk.get_metadata((2, 1, 2)) self.assertEqual((block, metadata), truthify_block(o, block, metadata)) return d def test_not_gate(self): """ NOT gates should work. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): for i, o in ((True, False), (False, True)): # Reset the hook. self.hook.asic = Asic() # The tableau. chunk.set_block((2, 1, 1), blocks["sand"].slot) chunk.set_block((3, 1, 1), blocks["redstone-torch"].slot) # Attach the lever to the sand block, and throw it. For sanity # purposes, grab the orientation metadata from the block # definition. orientation = blocks["lever"].orientation("-x") iblock, imetadata = truthify_block(i, blocks["lever"].slot, orientation) chunk.set_block((1, 1, 1), iblock) chunk.set_metadata((1, 1, 1), imetadata) # Attach the torch to the sand block too. orientation = blocks["redstone-torch"].orientation("+x") chunk.set_metadata((3, 1, 1), orientation) # Run the circuit, starting at the switch. self.hook.feed((1, 1, 1)) # Lever, torch, sand. self.hook.process() self.hook.process() self.hook.process() block = chunk.get_block((3, 1, 1)) metadata = chunk.get_metadata((3, 1, 1)) self.assertEqual((block, metadata), truthify_block(o, block, metadata)) return d
class TestWorldChunks(unittest.TestCase): def setUp(self): self.name = "unittest" self.d = tempfile.mkdtemp() self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "url", "file://%s" % self.d) self.bcp.set("world unittest", "serializer", "alpha") self.w = World(self.bcp, self.name) self.w.pipeline = [] self.w.start() def tearDown(self): self.w.stop() shutil.rmtree(self.d) def test_trivial(self): pass @inlineCallbacks def test_get_block(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.blocks = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.blocks.shape = (16, 16, 128) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. block = yield self.w.get_block((x, y, z)) self.assertEqual(block, chunk.get_block((x, y, z))) @inlineCallbacks def test_get_metadata(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.metadata = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.metadata.shape = (16, 16, 128) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. metadata = yield self.w.get_metadata((x, y, z)) self.assertEqual(metadata, chunk.get_metadata((x, y, z))) @inlineCallbacks def test_get_block_readback(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.blocks = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.blocks.shape = (16, 16, 128) # Evict the chunk and grab it again. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(0, 0) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. block = yield self.w.get_block((x, y, z)) self.assertEqual(block, chunk.get_block((x, y, z))) @inlineCallbacks def test_get_block_readback_negative(self): chunk = yield self.w.request_chunk(-1, -1) # Fill the chunk with random stuff. chunk.blocks = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.blocks.shape = (16, 16, 128) # Evict the chunk and grab it again. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(-1, -1) for x, y, z in product(xrange(2), repeat=3): block = yield self.w.get_block((x - 16, y, z - 16)) self.assertEqual(block, chunk.get_block((x, y, z))) @inlineCallbacks def test_get_metadata_readback(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.metadata = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.metadata.shape = (16, 16, 128) # Evict the chunk and grab it again. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(0, 0) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. metadata = yield self.w.get_metadata((x, y, z)) self.assertEqual(metadata, chunk.get_metadata((x, y, z))) @inlineCallbacks def test_world_level_mark_chunk_dirty(self): chunk = yield self.w.request_chunk(0, 0) # Reload chunk. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(0, 0) self.assertFalse(chunk.dirty) self.w.mark_dirty((12, 64, 4)) chunk = yield self.w.request_chunk(0, 0) self.assertTrue(chunk.dirty) @inlineCallbacks def test_world_level_mark_chunk_dirty_offset(self): chunk = yield self.w.request_chunk(1, 2) # Reload chunk. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(1, 2) self.assertFalse(chunk.dirty) self.w.mark_dirty((29, 64, 43)) chunk = yield self.w.request_chunk(1, 2) self.assertTrue(chunk.dirty) @inlineCallbacks def test_sync_get_block(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.blocks = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.blocks.shape = (16, 16, 128) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. block = self.w.sync_get_block((x, y, z)) self.assertEqual(block, chunk.get_block((x, y, z))) def test_sync_get_block_unloaded(self): self.assertRaises(ChunkNotLoaded, self.w.sync_get_block, (0, 0, 0)) def test_sync_get_metadata_neighboring(self): """ Even if a neighboring chunk is loaded, the target chunk could still be unloaded. Test with sync_get_metadata() to increase test coverage. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): self.assertRaises(ChunkNotLoaded, self.w.sync_get_metadata, (16, 0, 0)) return d
class TestBravoFactory(unittest.TestCase): def setUp(self): # Same setup as World, because Factory is very automagical. self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "port", "0") self.bcp.set("world unittest", "mode", "creative") self.f = BravoFactory(self.bcp, self.name) def test_trivial(self): pass def test_initial_attributes(self): """ Make sure that the basic attributes of the factory are correct. You'd be surprised how often this test breaks. """ self.assertEqual(self.f.name, "unittest") self.assertEqual(self.f.config_name, "world unittest") self.assertEqual(self.f.eid, 1) def test_update_time(self): """ Timekeeping should work. """ clock = Clock() clock.advance(20) self.patch(reactor, "seconds", clock.seconds) self.patch(self.f, "update_season", lambda: None) self.f.timestamp = 0 self.f.time = 0 self.f.update_time() self.assertEqual(self.f.timestamp, 20) self.assertEqual(self.f.time, 400) def test_update_time_by_day(self): """ Timekeeping should be alright with more than a day passing at once. """ clock = Clock() clock.advance(1201) self.patch(reactor, "seconds", clock.seconds) self.patch(self.f, "update_season", lambda: None) self.f.timestamp = 0 self.f.time = 0 self.f.day = 0 self.f.update_time() self.assertEqual(self.f.time, 20) self.assertEqual(self.f.day, 1) def test_update_season_empty(self): """ If no seasons are enabled, things should proceed as normal. """ self.bcp.set("world unittest", "seasons", "") self.f.register_plugins() self.f.day = 0 self.f.update_season() self.assertTrue(self.f.world.season is None) self.f.day = 90 self.f.update_season() self.assertTrue(self.f.world.season is None) def test_update_season_winter(self): """ If winter is the only season available, then only winter should be selected, regardless of day. """ self.bcp.set("world unittest", "seasons", "winter") self.f.register_plugins() self.f.day = 0 self.f.update_season() self.assertEqual(self.f.world.season.name, "winter") self.f.day = 90 self.f.update_season() self.assertEqual(self.f.world.season.name, "winter") def test_update_season_switch(self): """ The season should change from spring to winter when both are enabled. """ self.bcp.set("world unittest", "seasons", "winter, spring") self.f.register_plugins() self.f.day = 0 self.f.update_season() self.assertEqual(self.f.world.season.name, "winter") self.f.day = 90 self.f.update_season() self.assertEqual(self.f.world.season.name, "spring") def test_set_username(self): p = MockProtocol(None) p.username = "******" self.f.protocols["Hurp"] = p self.assertTrue(self.f.set_username(p, "Derp")) self.assertTrue("Derp" in self.f.protocols) self.assertTrue("Hurp" not in self.f.protocols) self.assertEqual(p.username, "Derp") def test_set_username_taken(self): p = MockProtocol(None) p.username = "******" self.f.protocols["Hurp"] = p self.f.protocols["Derp"] = None self.assertFalse(self.f.set_username(p, "Derp")) self.assertEqual(p.username, "Hurp") def test_set_username_noop(self): p = MockProtocol(None) p.username = "******" self.f.protocols["Hurp"] = p self.assertFalse(self.f.set_username(p, "Hurp"))
class TestBravoFactoryStarted(unittest.TestCase): """ Tests which require ``startFactory()`` to be called. """ def setUp(self): # Same setup as World, because Factory is very automagical. self.d = tempfile.mkdtemp() self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") d = { "authenticator": "offline", "automatons": "", "generators": "", "mode": "creative", "port": "0", "seasons": "winter, spring", "serializer": "alpha", "url": "file://%s" % self.d, } for k, v in d.items(): self.bcp.set("world unittest", k, v) self.f = BravoFactory(self.bcp, self.name) # And now start the factory. self.f.startFactory() def tearDown(self): self.f.stopFactory() shutil.rmtree(self.d) def test_trivial(self): pass def test_create_entity_pickup(self): entity = self.f.create_entity(0, 0, 0, "Item") self.assertEqual(entity.eid, 2) self.assertEqual(self.f.eid, 2) def test_create_entity_player(self): entity = self.f.create_entity(0, 0, 0, "Player", username="******") self.assertEqual(entity.eid, 2) self.assertEqual(entity.username, "unittest") self.assertEqual(self.f.eid, 2) def test_give(self): self.f.give((0, 0, 0), (2, 0), 1) def test_give_oversized(self): """ Check that oversized inputs to ``give()`` merely cause lots of pickups to be spawned. """ # Our check consists of counting the number of times broadcast is # called. count = [0] def broadcast(packet): count[0] += 1 self.patch(self.f, "broadcast", broadcast) # 65 blocks should be split into two stacks. self.f.give((0, 0, 0), (2, 0), 65) self.assertEqual(count[0], 2) def test_players_near(self): # Register some protocols with a player on the factory first. players = [ self.f.create_entity(0, 0, 0, "Player", username=""), # eid 2 self.f.create_entity(0, 2, 0, "Player", username=""), # eid 3 self.f.create_entity(1, 0, 3, "Player", username=""), # eid 4 self.f.create_entity(0, 4, 1, "Player", username=""), # eid 5 ] for i, player in enumerate(players): self.f.protocols[i] = MockProtocol(player) # List of tests (player in the center, radius, expected eids). expected_results = [ (players[0], 1, []), (players[0], 2, [3]), (players[0], 4, [3, 4]), (players[0], 5, [3, 4, 5]), (players[1], 3, [2, 5]), ] for player, radius, result in expected_results: found = [p.eid for p in self.f.players_near(player, radius)] self.assertEqual(set(found), set(result))
class TestBravoFactoryStarted(unittest.TestCase): """ Tests which require ``startFactory()`` to be called. """ def setUp(self): # Same setup as World, because Factory is very automagical. self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") d = { "automatons" : "", "generators" : "", "mode" : "creative", "port" : "0", "seasons" : "winter, spring", "serializer" : "memory", "url" : "", } for k, v in d.items(): self.bcp.set("world unittest", k, v) self.f = BravoFactory(self.bcp, self.name) # And now start the factory. self.f.startFactory() def tearDown(self): self.f.stopFactory() def test_trivial(self): pass def test_create_entity_pickup(self): entity = self.f.create_entity(0, 0, 0, "Item") self.assertEqual(entity.eid, 2) self.assertEqual(self.f.eid, 2) def test_create_entity_player(self): entity = self.f.create_entity(0, 0, 0, "Player", username="******") self.assertEqual(entity.eid, 2) self.assertEqual(entity.username, "unittest") self.assertEqual(self.f.eid, 2) def test_give(self): self.f.give((0, 0, 0), (2, 0), 1) def test_give_oversized(self): """ Check that oversized inputs to ``give()`` merely cause lots of pickups to be spawned. """ # Our check consists of counting the number of times broadcast is # called. count = [0] def broadcast(packet): count[0] += 1 self.patch(self.f, "broadcast", broadcast) # 65 blocks should be split into two stacks. self.f.give((0, 0, 0), (2, 0), 65) self.assertEqual(count[0], 2) def test_players_near(self): # Register some protocols with a player on the factory first. players = [ self.f.create_entity(0, 0, 0, "Player", username=""), # eid 2 self.f.create_entity(0, 2, 0, "Player", username=""), # eid 3 self.f.create_entity(1, 0, 3, "Player", username=""), # eid 4 self.f.create_entity(0, 4, 1, "Player", username=""), # eid 5 ] for i, player in enumerate(players): self.f.protocols[i] = MockProtocol(player) # List of tests (player in the center, radius, expected eids). expected_results = [ (players[0], 1, []), (players[0], 2, [3]), (players[0], 4, [3, 4]), (players[0], 5, [3, 4, 5]), (players[1], 3, [2, 5]), ] for player, radius, result in expected_results: found = [p.eid for p in self.f.players_near(player, radius)] self.assertEqual(set(found), set(result))
class TestWorldChunks(unittest.TestCase): def setUp(self): self.name = "unittest" self.d = tempfile.mkdtemp() self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "url", "file://%s" % self.d) self.bcp.set("world unittest", "serializer", "alpha") self.w = World(self.bcp, self.name) self.w.pipeline = [] self.w.start() def tearDown(self): self.w.stop() shutil.rmtree(self.d) def test_trivial(self): pass @inlineCallbacks def test_get_block(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.blocks = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.blocks.shape = (16, 16, 128) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. block = yield self.w.get_block((x, y, z)) self.assertEqual(block, chunk.get_block((x, y, z))) @inlineCallbacks def test_get_metadata(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.metadata = numpy.fromstring(numpy.random.bytes( chunk.blocks.size), dtype=numpy.uint8) chunk.metadata.shape = (16, 16, 128) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. metadata = yield self.w.get_metadata((x, y, z)) self.assertEqual(metadata, chunk.get_metadata((x, y, z))) @inlineCallbacks def test_get_block_readback(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.blocks = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.blocks.shape = (16, 16, 128) # Evict the chunk and grab it again. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(0, 0) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. block = yield self.w.get_block((x, y, z)) self.assertEqual(block, chunk.get_block((x, y, z))) @inlineCallbacks def test_get_block_readback_negative(self): chunk = yield self.w.request_chunk(-1, -1) # Fill the chunk with random stuff. chunk.blocks = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.blocks.shape = (16, 16, 128) # Evict the chunk and grab it again. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(-1, -1) for x, y, z in product(xrange(2), repeat=3): block = yield self.w.get_block((x - 16, y, z - 16)) self.assertEqual(block, chunk.get_block((x, y, z))) @inlineCallbacks def test_get_metadata_readback(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.metadata = numpy.fromstring(numpy.random.bytes( chunk.blocks.size), dtype=numpy.uint8) chunk.metadata.shape = (16, 16, 128) # Evict the chunk and grab it again. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(0, 0) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. metadata = yield self.w.get_metadata((x, y, z)) self.assertEqual(metadata, chunk.get_metadata((x, y, z))) @inlineCallbacks def test_world_level_mark_chunk_dirty(self): chunk = yield self.w.request_chunk(0, 0) # Reload chunk. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(0, 0) self.assertFalse(chunk.dirty) self.w.mark_dirty((12, 64, 4)) chunk = yield self.w.request_chunk(0, 0) self.assertTrue(chunk.dirty) @inlineCallbacks def test_world_level_mark_chunk_dirty_offset(self): chunk = yield self.w.request_chunk(1, 2) # Reload chunk. self.w.save_chunk(chunk) del chunk self.w.chunk_cache.clear() self.w.dirty_chunk_cache.clear() chunk = yield self.w.request_chunk(1, 2) self.assertFalse(chunk.dirty) self.w.mark_dirty((29, 64, 43)) chunk = yield self.w.request_chunk(1, 2) self.assertTrue(chunk.dirty) @inlineCallbacks def test_sync_get_block(self): chunk = yield self.w.request_chunk(0, 0) # Fill the chunk with random stuff. chunk.blocks = numpy.fromstring(numpy.random.bytes(chunk.blocks.size), dtype=numpy.uint8) chunk.blocks.shape = (16, 16, 128) for x, y, z in product(xrange(2), repeat=3): # This works because the chunk is at (0, 0) so the coords don't # need to be adjusted. block = self.w.sync_get_block((x, y, z)) self.assertEqual(block, chunk.get_block((x, y, z))) def test_sync_get_block_unloaded(self): self.assertRaises(ChunkNotLoaded, self.w.sync_get_block, (0, 0, 0)) def test_sync_get_metadata_neighboring(self): """ Even if a neighboring chunk is loaded, the target chunk could still be unloaded. Test with sync_get_metadata() to increase test coverage. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): self.assertRaises(ChunkNotLoaded, self.w.sync_get_metadata, (16, 0, 0)) return d
if len(sys.argv) <= 3: print "Not enough arguments." sys.exit() d = retrieve_plugins(ITerrainGenerator) size = int(sys.argv[1]) pipeline = [d[name] for name in sys.argv[2].split(",")] target = sys.argv[3] print "Making map of %dx%d chunks in %s" % (size, size, target) print "Using pipeline: %s" % ", ".join(plugin.name for plugin in pipeline) config = BravoConfigParser() config.add_section("world mapgen") config.set("world mapgen", "url", target) config.set("world mapgen", "serializer", "beta") world = World(config, "mapgen") world.connect() world.pipeline = pipeline world.season = None world.saving = True counts = [1, 2, 4, 5, 8] count = 0 total = size**2 cpu = 0 before = time.time() for i, j in product(xrange(size), repeat=2):
class TestWater(TestCase): def setUp(self): # Set up world. self.name = "unittest" self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "url", "") self.bcp.set("world unittest", "serializer", "memory") self.w = World(self.bcp, self.name) self.w.pipeline = [] self.w.start() # And finally the mock factory. self.f = PhysicsMockFactory() self.f.world = self.w # Using dig hook to grab the plugin since the build hook was nuked in # favor of the automaton interface. self.p = bravo.plugin.retrieve_plugins(IDigHook, factory=self.f) self.hook = self.p["water"] def tearDown(self): self.w.stop() self.hook.stop() def test_trivial(self): pass def test_update_fluid_negative(self): """ update_fluid() should always return False for Y at the bottom of the world. """ self.assertFalse(self.hook.update_fluid(self.w, (0, -1, 0), False)) def test_update_fluid_unloaded(self): self.assertRaises(ChunkNotLoaded, self.hook.update_fluid, self.w, (0, 0, 0), False) def test_update_fluid(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): self.assertTrue(self.hook.update_fluid(self.w, (0, 0, 0), False)) self.assertEqual(self.w.sync_get_block((0, 0, 0)), blocks["water"].slot) self.assertEqual(self.w.sync_get_metadata((0, 0, 0)), 0) return d def test_update_fluid_metadata(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): self.assertTrue(self.hook.update_fluid(self.w, (0, 0, 0), False, 1)) self.assertEqual(self.w.sync_get_metadata((0, 0, 0)), 1) return d def test_update_fluid_falling(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): self.assertTrue(self.hook.update_fluid(self.w, (0, 0, 0), True)) self.assertEqual(self.w.sync_get_metadata((0, 0, 0)), 8) return d def test_zero_y(self): """ Double-check that water placed on the very bottom of the world doesn't cause internal errors. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium; if any exceptions happen, # they will bubble up. while self.hook.tracked: self.hook.process() def test_spring_spread(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((1, 0, 1), blocks["spring"].slot) self.hook.tracked.add((1, 0, 1)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() for coords in ((2, 0, 1), (1, 0, 2), (0, 0, 1), (1, 0, 0)): self.assertEqual(chunk.get_block(coords), blocks["water"].slot) self.assertEqual(chunk.get_metadata(coords), 0x0) return d def test_spring_spread_edge(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() for coords in ((1, 0, 0), (0, 0, 1)): self.assertEqual(chunk.get_block(coords), blocks["water"].slot) self.assertEqual(chunk.get_metadata(coords), 0x0) return d def test_fluid_spread_edge(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() for coords in ((2, 0, 0), (1, 0, 1), (0, 0, 2)): self.assertEqual(chunk.get_block(coords), blocks["water"].slot) self.assertEqual(chunk.get_metadata(coords), 0x1) return d @inlineCallbacks def test_spring_fall(self): """ Falling water should appear below springs. """ self.w.set_block((0, 1, 0), blocks["spring"].slot) self.hook.tracked.add((0, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() block = yield self.w.get_block((0, 0, 0)) metadata = yield self.w.get_metadata((0, 0, 0)) self.assertEqual(block, blocks["water"].slot) self.assertEqual(metadata, 0x8) @inlineCallbacks def test_spring_fall_dig(self): """ Destroying ground underneath spring should allow water to continue falling downwards. """ self.w.set_block((0, 1, 0), blocks["spring"].slot) self.w.set_block((0, 0, 0), blocks["dirt"].slot) self.hook.tracked.add((0, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() #dig away dirt under spring self.w.destroy((0, 0, 0)) self.hook.tracked.add((0, 1, 0)) while self.hook.tracked: self.hook.process() block = yield self.w.get_block((0, 0, 0)) self.assertEqual(block, blocks["water"].slot) def test_spring_fall_dig_offset(self): """ Destroying ground next to a spring should cause a waterfall effect. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((1, 1, 0), blocks["spring"].slot) chunk.set_block((1, 0, 0), blocks["dirt"].slot) chunk.set_block((1, 0, 1), blocks["dirt"].slot) self.hook.tracked.add((1, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Dig away the dirt next to the dirt under the spring, and simulate # the dig hook by adding the block above it. chunk.destroy((1, 0, 1)) self.hook.tracked.add((1, 1, 1)) while self.hook.tracked: self.hook.process() self.assertEqual(chunk.get_block((1, 0, 1)), blocks["water"].slot) return d def test_trench(self): """ Fluid should not spread across the top of existing fluid. This test is for a specific kind of trench-digging pattern. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((0, 2, 0), blocks["spring"].slot) chunk.set_block((0, 1, 0), blocks["dirt"].slot) self.hook.tracked.add((0, 2, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Dig the dirt. self.w.destroy((0, 1, 0)) self.hook.tracked.add((0, 1, 1)) self.hook.tracked.add((0, 2, 0)) self.hook.tracked.add((1, 1, 0)) while self.hook.tracked: self.hook.process() block = chunk.get_block((0, 2, 2)) self.assertEqual(block, blocks["air"].slot) @inlineCallbacks def test_obstacle(self): """ Test that obstacles are flowed around correctly. """ yield self.w.set_block((0, 0, 0), blocks["spring"].slot) yield self.w.set_block((1, 0, 0), blocks["stone"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Make sure that the water level behind the stone is 0x3, not 0x0. metadata = yield self.w.get_metadata((2, 0, 0)) self.assertEqual(metadata, 0x3) @inlineCallbacks def test_sponge(self): """ Test that sponges prevent water from spreading near them. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.w.set_block((3, 0, 0), blocks["sponge"].slot) self.hook.tracked.add((0, 0, 0)) self.hook.tracked.add((3, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Make sure that water did not spread near the sponge. block = yield self.w.get_block((1, 0, 0)) self.assertNotEqual(block, blocks["water"].slot) def test_sponge_absorb_spring(self): """ Test that sponges can absorb springs and will cause all of the surrounding water to dry up. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() self.w.set_block((1, 0, 0), blocks["sponge"].slot) self.hook.tracked.add((1, 0, 0)) while self.hook.tracked: self.hook.process() for coords in ((0, 0, 0), (0, 0, 1)): block = yield self.w.get_block(coords) self.assertEqual(block, blocks["air"].slot) # Make sure that water did not spread near the sponge. block = yield self.w.get_block((1, 0, 0)) self.assertNotEqual(block, blocks["water"].slot) return d @inlineCallbacks def test_sponge_salt(self): """ Test that sponges don't "salt the earth" or have any kind of lasting effects after destruction. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() chunk = yield self.w.request_chunk(0, 0) # Take a snapshot at the base level, with a clever slice. before = chunk.sections[0].blocks[:256], chunk.sections[0].metadata[:256] self.w.set_block((3, 0, 0), blocks["sponge"].slot) self.hook.tracked.add((3, 0, 0)) while self.hook.tracked: self.hook.process() self.w.destroy((3, 0, 0)) self.hook.tracked.add((3, 0, 0)) while self.hook.tracked: self.hook.process() # Make another snapshot, for comparison. after = chunk.sections[0].blocks[:256], chunk.sections[0].metadata[:256] # Make sure that the sponge didn't permanently change anything. self.assertEqual(before, after) @inlineCallbacks def test_spring_remove(self): """ Test that water dries up if no spring is providing it. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Remove the spring. self.w.destroy((0, 0, 0)) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() for coords in ((1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)): block = yield self.w.get_block(coords) self.assertEqual(block, blocks["air"].slot) @inlineCallbacks def test_spring_underneath_keepalive(self): """ Test that springs located at a lower altitude than stray water do not keep that stray water alive. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.w.set_block((0, 1, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) self.hook.tracked.add((0, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Remove the upper spring. self.w.destroy((0, 1, 0)) self.hook.tracked.add((0, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Check that the upper water blocks dried out. Don't care about the # lower ones in this test. for coords in ((1, 1, 0), (-1, 1, 0), (0, 1, 1), (0, 1, -1)): block = yield self.w.get_block(coords) self.assertEqual(block, blocks["air"].slot)
class TestWater(unittest.TestCase): def setUp(self): # Set up world. self.name = "unittest" self.d = tempfile.mkdtemp() self.bcp = BravoConfigParser() self.bcp.add_section("world unittest") self.bcp.set("world unittest", "url", "file://%s" % self.d) self.bcp.set("world unittest", "serializer", "alpha") self.w = World(self.bcp, self.name) self.w.pipeline = [] self.w.start() # And finally the mock factory. self.f = PhysicsMockFactory() self.f.world = self.w # Using dig hook to grab the plugin since the build hook was nuked in # favor of the automaton interface. pp = {"factory": self.f} self.p = bravo.plugin.retrieve_plugins(IDigHook, parameters=pp) if "water" not in self.p: raise unittest.SkipTest("Plugin not present") self.hook = self.p["water"] def tearDown(self): self.w.stop() self.hook.stop() shutil.rmtree(self.d) def test_trivial(self): pass def test_update_fluid_negative(self): """ update_fluid() should always return False for Y at the bottom of the world. """ self.assertFalse(self.hook.update_fluid(self.w, (0, -1, 0), False)) def test_update_fluid_unloaded(self): self.assertRaises(ChunkNotLoaded, self.hook.update_fluid, self.w, (0, 0, 0), False) def test_update_fluid(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): self.assertTrue(self.hook.update_fluid(self.w, (0, 0, 0), False)) self.assertEqual(self.w.sync_get_block((0, 0, 0)), blocks["water"].slot) self.assertEqual(self.w.sync_get_metadata((0, 0, 0)), 0) return d def test_update_fluid_metadata(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): self.assertTrue(self.hook.update_fluid(self.w, (0, 0, 0), False, 1)) self.assertEqual(self.w.sync_get_metadata((0, 0, 0)), 1) return d def test_update_fluid_falling(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): self.assertTrue(self.hook.update_fluid(self.w, (0, 0, 0), True)) self.assertEqual(self.w.sync_get_metadata((0, 0, 0)), 8) return d def test_zero_y(self): """ Double-check that water placed on the very bottom of the world doesn't cause internal errors. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium; if any exceptions happen, # they will bubble up. while self.hook.tracked: self.hook.process() def test_spring_spread(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((1, 0, 1), blocks["spring"].slot) self.hook.tracked.add((1, 0, 1)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() for coords in ((2, 0, 1), (1, 0, 2), (0, 0, 1), (1, 0, 0)): self.assertEqual(chunk.get_block(coords), blocks["water"].slot) self.assertEqual(chunk.get_metadata(coords), 0x0) return d def test_spring_spread_edge(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() for coords in ((1, 0, 0), (0, 0, 1)): self.assertEqual(chunk.get_block(coords), blocks["water"].slot) self.assertEqual(chunk.get_metadata(coords), 0x0) return d def test_fluid_spread_edge(self): d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() for coords in ((2, 0, 0), (1, 0, 1), (0, 0, 2)): self.assertEqual(chunk.get_block(coords), blocks["water"].slot) self.assertEqual(chunk.get_metadata(coords), 0x1) return d @inlineCallbacks def test_spring_fall(self): """ Falling water should appear below springs. """ self.w.set_block((0, 1, 0), blocks["spring"].slot) self.hook.tracked.add((0, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() block = yield self.w.get_block((0, 0, 0)) metadata = yield self.w.get_metadata((0, 0, 0)) self.assertEqual(block, blocks["water"].slot) self.assertEqual(metadata, 0x8) @inlineCallbacks def test_spring_fall_dig(self): """ Destroying ground underneath spring should allow water to continue falling downwards. """ self.w.set_block((0, 1, 0), blocks["spring"].slot) self.w.set_block((0, 0, 0), blocks["dirt"].slot) self.hook.tracked.add((0, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() #dig away dirt under spring self.w.destroy((0, 0, 0)) self.hook.tracked.add((0, 1, 0)) while self.hook.tracked: self.hook.process() block = yield self.w.get_block((0, 0, 0)) self.assertEqual(block, blocks["water"].slot) def test_spring_fall_dig_offset(self): """ Destroying ground next to a spring should cause a waterfall effect. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((1, 1, 0), blocks["spring"].slot) chunk.set_block((1, 0, 0), blocks["dirt"].slot) chunk.set_block((1, 0, 1), blocks["dirt"].slot) self.hook.tracked.add((1, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Dig away the dirt next to the dirt under the spring, and simulate # the dig hook by adding the block above it. chunk.destroy((1, 0, 1)) self.hook.tracked.add((1, 1, 1)) while self.hook.tracked: self.hook.process() self.assertEqual(chunk.get_block((1, 0, 1)), blocks["water"].slot) return d def test_trench(self): """ Fluid should not spread across the top of existing fluid. This test is for a specific kind of trench-digging pattern. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((0, 2, 0), blocks["spring"].slot) chunk.set_block((0, 1, 0), blocks["dirt"].slot) self.hook.tracked.add((0, 2, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Dig the dirt. self.w.destroy((0, 1, 0)) self.hook.tracked.add((0, 1, 1)) self.hook.tracked.add((0, 2, 0)) self.hook.tracked.add((1, 1, 0)) while self.hook.tracked: self.hook.process() block = chunk.get_block((0, 2, 2)) self.assertEqual(block, blocks["air"].slot) @inlineCallbacks def test_obstacle(self): """ Test that obstacles are flowed around correctly. """ yield self.w.set_block((0, 0, 0), blocks["spring"].slot) yield self.w.set_block((1, 0, 0), blocks["stone"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Make sure that the water level behind the stone is 0x3, not 0x0. metadata = yield self.w.get_metadata((2, 0, 0)) self.assertEqual(metadata, 0x3) @inlineCallbacks def test_sponge(self): """ Test that sponges prevent water from spreading near them. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.w.set_block((3, 0, 0), blocks["sponge"].slot) self.hook.tracked.add((0, 0, 0)) self.hook.tracked.add((3, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Make sure that water did not spread near the sponge. block = yield self.w.get_block((1, 0, 0)) self.assertNotEqual(block, blocks["water"].slot) def test_sponge_absorb_spring(self): """ Test that sponges can absorb springs and will cause all of the surrounding water to dry up. """ d = self.w.request_chunk(0, 0) @d.addCallback def cb(chunk): chunk.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() self.w.set_block((1, 0, 0), blocks["sponge"].slot) self.hook.tracked.add((1, 0, 0)) while self.hook.tracked: self.hook.process() for coords in ((0, 0, 0), (0, 0, 1)): block = yield self.w.get_block(coords) self.assertEqual(block, blocks["air"].slot) # Make sure that water did not spread near the sponge. block = yield self.w.get_block((1, 0, 0)) self.assertNotEqual(block, blocks["water"].slot) return d @inlineCallbacks def test_sponge_salt(self): """ Test that sponges don't "salt the earth" or have any kind of lasting effects after destruction. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Take a snapshot. chunk = yield self.w.request_chunk(0, 0) before = chunk.blocks[:, :, 0], chunk.metadata[:, :, 0] self.w.set_block((3, 0, 0), blocks["sponge"].slot) self.hook.tracked.add((3, 0, 0)) while self.hook.tracked: self.hook.process() self.w.destroy((3, 0, 0)) self.hook.tracked.add((3, 0, 0)) while self.hook.tracked: self.hook.process() after = chunk.blocks[:, :, 0], chunk.metadata[:, :, 0] # Make sure that the sponge didn't permanently change anything. assert_array_equal(before, after) @inlineCallbacks def test_spring_remove(self): """ Test that water dries up if no spring is providing it. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Remove the spring. self.w.destroy((0, 0, 0)) self.hook.tracked.add((0, 0, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() for coords in ((1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)): block = yield self.w.get_block(coords) self.assertEqual(block, blocks["air"].slot) @inlineCallbacks def test_spring_underneath_keepalive(self): """ Test that springs located at a lower altitude than stray water do not keep that stray water alive. """ self.w.set_block((0, 0, 0), blocks["spring"].slot) self.w.set_block((0, 1, 0), blocks["spring"].slot) self.hook.tracked.add((0, 0, 0)) self.hook.tracked.add((0, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Remove the upper spring. self.w.destroy((0, 1, 0)) self.hook.tracked.add((0, 1, 0)) # Tight-loop run the hook to equilibrium. while self.hook.tracked: self.hook.process() # Check that the upper water blocks dried out. Don't care about the # lower ones in this test. for coords in ((1, 1, 0), (-1, 1, 0), (0, 1, 1), (0, 1, -1)): block = yield self.w.get_block(coords) self.assertEqual(block, blocks["air"].slot)