]> git.lizzy.rs Git - dragonfireclient.git/blob - src/unittest/test.cpp
Merge branch 'master' of https://github.com/minetest/minetest
[dragonfireclient.git] / src / unittest / test.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "test.h"
21
22 #include "client/sound.h"
23 #include "nodedef.h"
24 #include "itemdef.h"
25 #include "gamedef.h"
26 #include "modchannels.h"
27 #include "content/mods.h"
28 #include "util/numeric.h"
29 #include "porting.h"
30
31 content_t t_CONTENT_STONE;
32 content_t t_CONTENT_GRASS;
33 content_t t_CONTENT_TORCH;
34 content_t t_CONTENT_WATER;
35 content_t t_CONTENT_LAVA;
36 content_t t_CONTENT_BRICK;
37
38 ////////////////////////////////////////////////////////////////////////////////
39
40 ////
41 //// TestGameDef
42 ////
43
44 class TestGameDef : public IGameDef {
45 public:
46         TestGameDef();
47         ~TestGameDef();
48
49         IItemDefManager *getItemDefManager() { return m_itemdef; }
50         IWritableItemDefManager *getWritableItemDefManager() { return m_itemdef; }
51         const NodeDefManager *getNodeDefManager() { return m_nodedef; }
52         NodeDefManager *getWritableNodeDefManager() { return m_nodedef; }
53         ICraftDefManager *getCraftDefManager() { return m_craftdef; }
54         ITextureSource *getTextureSource() { return m_texturesrc; }
55         IShaderSource *getShaderSource() { return m_shadersrc; }
56         ISoundManager *getSoundManager() { return m_soundmgr; }
57         scene::ISceneManager *getSceneManager() { return m_scenemgr; }
58         IRollbackManager *getRollbackManager() { return m_rollbackmgr; }
59         EmergeManager *getEmergeManager() { return m_emergemgr; }
60
61         scene::IAnimatedMesh *getMesh(const std::string &filename) { return NULL; }
62         bool checkLocalPrivilege(const std::string &priv) { return false; }
63         u16 allocateUnknownNodeId(const std::string &name) { return 0; }
64
65         void defineSomeNodes();
66
67         virtual const std::vector<ModSpec> &getMods() const
68         {
69                 static std::vector<ModSpec> testmodspec;
70                 return testmodspec;
71         }
72         virtual const ModSpec* getModSpec(const std::string &modname) const { return NULL; }
73         virtual std::string getModStoragePath() const { return "."; }
74         virtual bool registerModStorage(ModMetadata *meta) { return true; }
75         virtual void unregisterModStorage(const std::string &name) {}
76         bool joinModChannel(const std::string &channel);
77         bool leaveModChannel(const std::string &channel);
78         bool sendModChannelMessage(const std::string &channel, const std::string &message);
79         ModChannel *getModChannel(const std::string &channel)
80         {
81                 return m_modchannel_mgr->getModChannel(channel);
82         }
83
84 private:
85         IWritableItemDefManager *m_itemdef = nullptr;
86         NodeDefManager *m_nodedef = nullptr;
87         ICraftDefManager *m_craftdef = nullptr;
88         ITextureSource *m_texturesrc = nullptr;
89         IShaderSource *m_shadersrc = nullptr;
90         ISoundManager *m_soundmgr = nullptr;
91         scene::ISceneManager *m_scenemgr = nullptr;
92         IRollbackManager *m_rollbackmgr = nullptr;
93         EmergeManager *m_emergemgr = nullptr;
94         std::unique_ptr<ModChannelMgr> m_modchannel_mgr;
95 };
96
97
98 TestGameDef::TestGameDef() :
99         m_modchannel_mgr(new ModChannelMgr())
100 {
101         m_itemdef = createItemDefManager();
102         m_nodedef = createNodeDefManager();
103
104         defineSomeNodes();
105 }
106
107
108 TestGameDef::~TestGameDef()
109 {
110         delete m_itemdef;
111         delete m_nodedef;
112 }
113
114
115 void TestGameDef::defineSomeNodes()
116 {
117         IWritableItemDefManager *idef = (IWritableItemDefManager *)m_itemdef;
118         NodeDefManager *ndef = (NodeDefManager *)m_nodedef;
119
120         ItemDefinition itemdef;
121         ContentFeatures f;
122
123         //// Stone
124         itemdef = ItemDefinition();
125         itemdef.type = ITEM_NODE;
126         itemdef.name = "default:stone";
127         itemdef.description = "Stone";
128         itemdef.groups["cracky"] = 3;
129         itemdef.inventory_image = "[inventorycube"
130                 "{default_stone.png"
131                 "{default_stone.png"
132                 "{default_stone.png";
133         f = ContentFeatures();
134         f.name = itemdef.name;
135         for (TileDef &tiledef : f.tiledef)
136                 tiledef.name = "default_stone.png";
137         f.is_ground_content = true;
138         idef->registerItem(itemdef);
139         t_CONTENT_STONE = ndef->set(f.name, f);
140
141         //// Grass
142         itemdef = ItemDefinition();
143         itemdef.type = ITEM_NODE;
144         itemdef.name = "default:dirt_with_grass";
145         itemdef.description = "Dirt with grass";
146         itemdef.groups["crumbly"] = 3;
147         itemdef.inventory_image = "[inventorycube"
148                 "{default_grass.png"
149                 "{default_dirt.png&default_grass_side.png"
150                 "{default_dirt.png&default_grass_side.png";
151         f = ContentFeatures();
152         f.name = itemdef.name;
153         f.tiledef[0].name = "default_grass.png";
154         f.tiledef[1].name = "default_dirt.png";
155         for(int i = 2; i < 6; i++)
156                 f.tiledef[i].name = "default_dirt.png^default_grass_side.png";
157         f.is_ground_content = true;
158         idef->registerItem(itemdef);
159         t_CONTENT_GRASS = ndef->set(f.name, f);
160
161         //// Torch (minimal definition for lighting tests)
162         itemdef = ItemDefinition();
163         itemdef.type = ITEM_NODE;
164         itemdef.name = "default:torch";
165         f = ContentFeatures();
166         f.name = itemdef.name;
167         f.param_type = CPT_LIGHT;
168         f.light_propagates = true;
169         f.sunlight_propagates = true;
170         f.light_source = LIGHT_MAX-1;
171         idef->registerItem(itemdef);
172         t_CONTENT_TORCH = ndef->set(f.name, f);
173
174         //// Water
175         itemdef = ItemDefinition();
176         itemdef.type = ITEM_NODE;
177         itemdef.name = "default:water";
178         itemdef.description = "Water";
179         itemdef.inventory_image = "[inventorycube"
180                 "{default_water.png"
181                 "{default_water.png"
182                 "{default_water.png";
183         f = ContentFeatures();
184         f.name = itemdef.name;
185         f.alpha = 128;
186         f.liquid_type = LIQUID_SOURCE;
187         f.liquid_viscosity = 4;
188         f.is_ground_content = true;
189         f.groups["liquids"] = 3;
190         for (TileDef &tiledef : f.tiledef)
191                 tiledef.name = "default_water.png";
192         idef->registerItem(itemdef);
193         t_CONTENT_WATER = ndef->set(f.name, f);
194
195         //// Lava
196         itemdef = ItemDefinition();
197         itemdef.type = ITEM_NODE;
198         itemdef.name = "default:lava";
199         itemdef.description = "Lava";
200         itemdef.inventory_image = "[inventorycube"
201                 "{default_lava.png"
202                 "{default_lava.png"
203                 "{default_lava.png";
204         f = ContentFeatures();
205         f.name = itemdef.name;
206         f.alpha = 128;
207         f.liquid_type = LIQUID_SOURCE;
208         f.liquid_viscosity = 7;
209         f.light_source = LIGHT_MAX-1;
210         f.is_ground_content = true;
211         f.groups["liquids"] = 3;
212         for (TileDef &tiledef : f.tiledef)
213                 tiledef.name = "default_lava.png";
214         idef->registerItem(itemdef);
215         t_CONTENT_LAVA = ndef->set(f.name, f);
216
217
218         //// Brick
219         itemdef = ItemDefinition();
220         itemdef.type = ITEM_NODE;
221         itemdef.name = "default:brick";
222         itemdef.description = "Brick";
223         itemdef.groups["cracky"] = 3;
224         itemdef.inventory_image = "[inventorycube"
225                 "{default_brick.png"
226                 "{default_brick.png"
227                 "{default_brick.png";
228         f = ContentFeatures();
229         f.name = itemdef.name;
230         for (TileDef &tiledef : f.tiledef)
231                 tiledef.name = "default_brick.png";
232         f.is_ground_content = true;
233         idef->registerItem(itemdef);
234         t_CONTENT_BRICK = ndef->set(f.name, f);
235 }
236
237 bool TestGameDef::joinModChannel(const std::string &channel)
238 {
239         return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER);
240 }
241
242 bool TestGameDef::leaveModChannel(const std::string &channel)
243 {
244         return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
245 }
246
247 bool TestGameDef::sendModChannelMessage(const std::string &channel,
248         const std::string &message)
249 {
250         if (!m_modchannel_mgr->channelRegistered(channel))
251                 return false;
252
253         return true;
254 }
255
256 ////
257 //// run_tests
258 ////
259
260 bool run_tests()
261 {
262         u64 t1 = porting::getTimeMs();
263         TestGameDef gamedef;
264
265         g_logger.setLevelSilenced(LL_ERROR, true);
266
267         u32 num_modules_failed     = 0;
268         u32 num_total_tests_failed = 0;
269         u32 num_total_tests_run    = 0;
270         std::vector<TestBase *> &testmods = TestManager::getTestModules();
271         for (size_t i = 0; i != testmods.size(); i++) {
272                 if (!testmods[i]->testModule(&gamedef))
273                         num_modules_failed++;
274
275                 num_total_tests_failed += testmods[i]->num_tests_failed;
276                 num_total_tests_run += testmods[i]->num_tests_run;
277         }
278
279         u64 tdiff = porting::getTimeMs() - t1;
280
281         g_logger.setLevelSilenced(LL_ERROR, false);
282
283         const char *overall_status = (num_modules_failed == 0) ? "PASSED" : "FAILED";
284
285         rawstream
286                 << "++++++++++++++++++++++++++++++++++++++++"
287                 << "++++++++++++++++++++++++++++++++++++++++" << std::endl
288                 << "Unit Test Results: " << overall_status << std::endl
289                 << "    " << num_modules_failed << " / " << testmods.size()
290                 << " failed modules (" << num_total_tests_failed << " / "
291                 << num_total_tests_run << " failed individual tests)." << std::endl
292                 << "    Testing took " << tdiff << "ms total." << std::endl
293                 << "++++++++++++++++++++++++++++++++++++++++"
294                 << "++++++++++++++++++++++++++++++++++++++++" << std::endl;
295
296         return num_modules_failed;
297 }
298
299 ////
300 //// TestBase
301 ////
302
303 bool TestBase::testModule(IGameDef *gamedef)
304 {
305         rawstream << "======== Testing module " << getName() << std::endl;
306         u64 t1 = porting::getTimeMs();
307
308
309         runTests(gamedef);
310
311         u64 tdiff = porting::getTimeMs() - t1;
312         rawstream << "======== Module " << getName() << " "
313                 << (num_tests_failed ? "failed" : "passed") << " (" << num_tests_failed
314                 << " failures / " << num_tests_run << " tests) - " << tdiff
315                 << "ms" << std::endl;
316
317         if (!m_test_dir.empty())
318                 fs::RecursiveDelete(m_test_dir);
319
320         return num_tests_failed == 0;
321 }
322
323 std::string TestBase::getTestTempDirectory()
324 {
325         if (!m_test_dir.empty())
326                 return m_test_dir;
327
328         char buf[32];
329         porting::mt_snprintf(buf, sizeof(buf), "%08X", myrand());
330
331         m_test_dir = fs::TempPath() + DIR_DELIM "mttest_" + buf;
332         if (!fs::CreateDir(m_test_dir))
333                 throw TestFailedException();
334
335         return m_test_dir;
336 }
337
338 std::string TestBase::getTestTempFile()
339 {
340         char buf[32];
341         porting::mt_snprintf(buf, sizeof(buf), "%08X", myrand());
342
343         return getTestTempDirectory() + DIR_DELIM + buf + ".tmp";
344 }
345
346
347 /*
348         NOTE: These tests became non-working then NodeContainer was removed.
349               These should be redone, utilizing some kind of a virtual
350                   interface for Map (IMap would be fine).
351 */
352 #if 0
353 struct TestMapBlock: public TestBase
354 {
355         class TC : public NodeContainer
356         {
357         public:
358
359                 MapNode node;
360                 bool position_valid;
361                 core::list<v3s16> validity_exceptions;
362
363                 TC()
364                 {
365                         position_valid = true;
366                 }
367
368                 virtual bool isValidPosition(v3s16 p)
369                 {
370                         //return position_valid ^ (p==position_valid_exception);
371                         bool exception = false;
372                         for(core::list<v3s16>::Iterator i=validity_exceptions.begin();
373                                         i != validity_exceptions.end(); i++)
374                         {
375                                 if(p == *i)
376                                 {
377                                         exception = true;
378                                         break;
379                                 }
380                         }
381                         return exception ? !position_valid : position_valid;
382                 }
383
384                 virtual MapNode getNode(v3s16 p)
385                 {
386                         if(isValidPosition(p) == false)
387                                 throw InvalidPositionException();
388                         return node;
389                 }
390
391                 virtual void setNode(v3s16 p, MapNode & n)
392                 {
393                         if(isValidPosition(p) == false)
394                                 throw InvalidPositionException();
395                 };
396
397                 virtual u16 nodeContainerId() const
398                 {
399                         return 666;
400                 }
401         };
402
403         void Run()
404         {
405                 TC parent;
406
407                 MapBlock b(&parent, v3s16(1,1,1));
408                 v3s16 relpos(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
409
410                 UASSERT(b.getPosRelative() == relpos);
411
412                 UASSERT(b.getBox().MinEdge.X == MAP_BLOCKSIZE);
413                 UASSERT(b.getBox().MaxEdge.X == MAP_BLOCKSIZE*2-1);
414                 UASSERT(b.getBox().MinEdge.Y == MAP_BLOCKSIZE);
415                 UASSERT(b.getBox().MaxEdge.Y == MAP_BLOCKSIZE*2-1);
416                 UASSERT(b.getBox().MinEdge.Z == MAP_BLOCKSIZE);
417                 UASSERT(b.getBox().MaxEdge.Z == MAP_BLOCKSIZE*2-1);
418
419                 UASSERT(b.isValidPosition(v3s16(0,0,0)) == true);
420                 UASSERT(b.isValidPosition(v3s16(-1,0,0)) == false);
421                 UASSERT(b.isValidPosition(v3s16(-1,-142,-2341)) == false);
422                 UASSERT(b.isValidPosition(v3s16(-124,142,2341)) == false);
423                 UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true);
424                 UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE,MAP_BLOCKSIZE-1)) == false);
425
426                 /*
427                         TODO: this method should probably be removed
428                         if the block size isn't going to be set variable
429                 */
430                 /*UASSERT(b.getSizeNodes() == v3s16(MAP_BLOCKSIZE,
431                                 MAP_BLOCKSIZE, MAP_BLOCKSIZE));*/
432
433                 // Changed flag should be initially set
434                 UASSERT(b.getModified() == MOD_STATE_WRITE_NEEDED);
435                 b.resetModified();
436                 UASSERT(b.getModified() == MOD_STATE_CLEAN);
437
438                 // All nodes should have been set to
439                 // .d=CONTENT_IGNORE and .getLight() = 0
440                 for(u16 z=0; z<MAP_BLOCKSIZE; z++)
441                 for(u16 y=0; y<MAP_BLOCKSIZE; y++)
442                 for(u16 x=0; x<MAP_BLOCKSIZE; x++)
443                 {
444                         //UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_AIR);
445                         UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_IGNORE);
446                         UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_DAY) == 0);
447                         UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_NIGHT) == 0);
448                 }
449
450                 {
451                         MapNode n(CONTENT_AIR);
452                         for(u16 z=0; z<MAP_BLOCKSIZE; z++)
453                         for(u16 y=0; y<MAP_BLOCKSIZE; y++)
454                         for(u16 x=0; x<MAP_BLOCKSIZE; x++)
455                         {
456                                 b.setNode(v3s16(x,y,z), n);
457                         }
458                 }
459
460                 /*
461                         Parent fetch functions
462                 */
463                 parent.position_valid = false;
464                 parent.node.setContent(5);
465
466                 MapNode n;
467
468                 // Positions in the block should still be valid
469                 UASSERT(b.isValidPositionParent(v3s16(0,0,0)) == true);
470                 UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true);
471                 n = b.getNodeParent(v3s16(0,MAP_BLOCKSIZE-1,0));
472                 UASSERT(n.getContent() == CONTENT_AIR);
473
474                 // ...but outside the block they should be invalid
475                 UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == false);
476                 UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == false);
477                 UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == false);
478
479                 {
480                         bool exception_thrown = false;
481                         try{
482                                 // This should throw an exception
483                                 MapNode n = b.getNodeParent(v3s16(0,0,-1));
484                         }
485                         catch(InvalidPositionException &e)
486                         {
487                                 exception_thrown = true;
488                         }
489                         UASSERT(exception_thrown);
490                 }
491
492                 parent.position_valid = true;
493                 // Now the positions outside should be valid
494                 UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == true);
495                 UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == true);
496                 UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == true);
497                 n = b.getNodeParent(v3s16(0,0,MAP_BLOCKSIZE));
498                 UASSERT(n.getContent() == 5);
499
500                 /*
501                         Set a node
502                 */
503                 v3s16 p(1,2,0);
504                 n.setContent(4);
505                 b.setNode(p, n);
506                 UASSERT(b.getNode(p).getContent() == 4);
507                 //TODO: Update to new system
508                 /*UASSERT(b.getNodeTile(p) == 4);
509                 UASSERT(b.getNodeTile(v3s16(-1,-1,0)) == 5);*/
510
511                 /*
512                         propagateSunlight()
513                 */
514                 // Set lighting of all nodes to 0
515                 for(u16 z=0; z<MAP_BLOCKSIZE; z++){
516                         for(u16 y=0; y<MAP_BLOCKSIZE; y++){
517                                 for(u16 x=0; x<MAP_BLOCKSIZE; x++){
518                                         MapNode n = b.getNode(v3s16(x,y,z));
519                                         n.setLight(LIGHTBANK_DAY, 0);
520                                         n.setLight(LIGHTBANK_NIGHT, 0);
521                                         b.setNode(v3s16(x,y,z), n);
522                                 }
523                         }
524                 }
525                 {
526                         /*
527                                 Check how the block handles being a lonely sky block
528                         */
529                         parent.position_valid = true;
530                         b.setIsUnderground(false);
531                         parent.node.setContent(CONTENT_AIR);
532                         parent.node.setLight(LIGHTBANK_DAY, LIGHT_SUN);
533                         parent.node.setLight(LIGHTBANK_NIGHT, 0);
534                         core::map<v3s16, bool> light_sources;
535                         // The bottom block is invalid, because we have a shadowing node
536                         UASSERT(b.propagateSunlight(light_sources) == false);
537                         UASSERT(b.getNode(v3s16(1,4,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
538                         UASSERT(b.getNode(v3s16(1,3,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
539                         UASSERT(b.getNode(v3s16(1,2,0)).getLight(LIGHTBANK_DAY) == 0);
540                         UASSERT(b.getNode(v3s16(1,1,0)).getLight(LIGHTBANK_DAY) == 0);
541                         UASSERT(b.getNode(v3s16(1,0,0)).getLight(LIGHTBANK_DAY) == 0);
542                         UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
543                         UASSERT(b.getFaceLight2(1000, p, v3s16(0,1,0)) == LIGHT_SUN);
544                         UASSERT(b.getFaceLight2(1000, p, v3s16(0,-1,0)) == 0);
545                         UASSERT(b.getFaceLight2(0, p, v3s16(0,-1,0)) == 0);
546                         // According to MapBlock::getFaceLight,
547                         // The face on the z+ side should have double-diminished light
548                         //UASSERT(b.getFaceLight(p, v3s16(0,0,1)) == diminish_light(diminish_light(LIGHT_MAX)));
549                         // The face on the z+ side should have diminished light
550                         UASSERT(b.getFaceLight2(1000, p, v3s16(0,0,1)) == diminish_light(LIGHT_MAX));
551                 }
552                 /*
553                         Check how the block handles being in between blocks with some non-sunlight
554                         while being underground
555                 */
556                 {
557                         // Make neighbours to exist and set some non-sunlight to them
558                         parent.position_valid = true;
559                         b.setIsUnderground(true);
560                         parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);
561                         core::map<v3s16, bool> light_sources;
562                         // The block below should be valid because there shouldn't be
563                         // sunlight in there either
564                         UASSERT(b.propagateSunlight(light_sources, true) == true);
565                         // Should not touch nodes that are not affected (that is, all of them)
566                         //UASSERT(b.getNode(v3s16(1,2,3)).getLight() == LIGHT_SUN);
567                         // Should set light of non-sunlighted blocks to 0.
568                         UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == 0);
569                 }
570                 /*
571                         Set up a situation where:
572                         - There is only air in this block
573                         - There is a valid non-sunlighted block at the bottom, and
574                         - Invalid blocks elsewhere.
575                         - the block is not underground.
576
577                         This should result in bottom block invalidity
578                 */
579                 {
580                         b.setIsUnderground(false);
581                         // Clear block
582                         for(u16 z=0; z<MAP_BLOCKSIZE; z++){
583                                 for(u16 y=0; y<MAP_BLOCKSIZE; y++){
584                                         for(u16 x=0; x<MAP_BLOCKSIZE; x++){
585                                                 MapNode n;
586                                                 n.setContent(CONTENT_AIR);
587                                                 n.setLight(LIGHTBANK_DAY, 0);
588                                                 b.setNode(v3s16(x,y,z), n);
589                                         }
590                                 }
591                         }
592                         // Make neighbours invalid
593                         parent.position_valid = false;
594                         // Add exceptions to the top of the bottom block
595                         for(u16 x=0; x<MAP_BLOCKSIZE; x++)
596                         for(u16 z=0; z<MAP_BLOCKSIZE; z++)
597                         {
598                                 parent.validity_exceptions.push_back(v3s16(MAP_BLOCKSIZE+x, MAP_BLOCKSIZE-1, MAP_BLOCKSIZE+z));
599                         }
600                         // Lighting value for the valid nodes
601                         parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);
602                         core::map<v3s16, bool> light_sources;
603                         // Bottom block is not valid
604                         UASSERT(b.propagateSunlight(light_sources) == false);
605                 }
606         }
607 };
608
609 struct TestMapSector: public TestBase
610 {
611         class TC : public NodeContainer
612         {
613         public:
614
615                 MapNode node;
616                 bool position_valid;
617
618                 TC()
619                 {
620                         position_valid = true;
621                 }
622
623                 virtual bool isValidPosition(v3s16 p)
624                 {
625                         return position_valid;
626                 }
627
628                 virtual MapNode getNode(v3s16 p)
629                 {
630                         if(position_valid == false)
631                                 throw InvalidPositionException();
632                         return node;
633                 }
634
635                 virtual void setNode(v3s16 p, MapNode & n)
636                 {
637                         if(position_valid == false)
638                                 throw InvalidPositionException();
639                 };
640
641                 virtual u16 nodeContainerId() const
642                 {
643                         return 666;
644                 }
645         };
646
647         void Run()
648         {
649                 TC parent;
650                 parent.position_valid = false;
651
652                 // Create one with no heightmaps
653                 ServerMapSector sector(&parent, v2s16(1,1));
654
655                 UASSERT(sector.getBlockNoCreateNoEx(0) == nullptr);
656                 UASSERT(sector.getBlockNoCreateNoEx(1) == nullptr);
657
658                 MapBlock * bref = sector.createBlankBlock(-2);
659
660                 UASSERT(sector.getBlockNoCreateNoEx(0) == nullptr);
661                 UASSERT(sector.getBlockNoCreateNoEx(-2) == bref);
662
663                 //TODO: Check for AlreadyExistsException
664
665                 /*bool exception_thrown = false;
666                 try{
667                         sector.getBlock(0);
668                 }
669                 catch(InvalidPositionException &e){
670                         exception_thrown = true;
671                 }
672                 UASSERT(exception_thrown);*/
673
674         }
675 };
676 #endif