Skip to content

Commit 2dba29e

Browse files
committedMay 8, 2015
Tests: Add schematic unittests
Improve schematic file-saving interface Add ability to create temporary test files
1 parent 33c1141 commit 2dba29e

File tree

8 files changed

+329
-32
lines changed

8 files changed

+329
-32
lines changed
 

‎build/android/jni/Android.mk

+1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ LOCAL_SRC_FILES := \
224224
jni/src/unittest/test_objdef.cpp \
225225
jni/src/unittest/test_profiler.cpp \
226226
jni/src/unittest/test_random.cpp \
227+
jni/src/unittest/test_schematic.cpp \
227228
jni/src/unittest/test_serialization.cpp \
228229
jni/src/unittest/test_settings.cpp \
229230
jni/src/unittest/test_socket.cpp \

‎src/mg_schematic.cpp

+16-12
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ void Schematic::resolveNodeNames()
9494
}
9595

9696

97-
void Schematic::blitToVManip(v3s16 p, MMVManip *vm,
98-
Rotation rot, bool force_placement)
97+
void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_place)
9998
{
10099
sanity_check(m_ndef != NULL);
101100

@@ -151,7 +150,7 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm,
151150
if (schemdata[i].param1 == MTSCHEM_PROB_NEVER)
152151
continue;
153152

154-
if (!force_placement) {
153+
if (!force_place) {
155154
content_t c = vm->m_data[vi].getContent();
156155
if (c != CONTENT_AIR && c != CONTENT_IGNORE)
157156
continue;
@@ -174,7 +173,7 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm,
174173

175174

176175
void Schematic::placeStructure(Map *map, v3s16 p, u32 flags,
177-
Rotation rot, bool force_placement)
176+
Rotation rot, bool force_place)
178177
{
179178
assert(schemdata != NULL); // Pre-condition
180179
sanity_check(m_ndef != NULL);
@@ -198,7 +197,7 @@ void Schematic::placeStructure(Map *map, v3s16 p, u32 flags,
198197
v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1));
199198
vm->initialEmerge(bp1, bp2);
200199

201-
blitToVManip(p, vm, rot, force_placement);
200+
blitToVManip(p, vm, rot, force_place);
202201

203202
std::map<v3s16, MapBlock *> lighting_modified_blocks;
204203
std::map<v3s16, MapBlock *> modified_blocks;
@@ -405,35 +404,40 @@ bool Schematic::loadSchematicFromFile(const std::string &filename,
405404
}
406405

407406

408-
bool Schematic::saveSchematicToFile(const std::string &filename)
407+
bool Schematic::saveSchematicToFile(const std::string &filename,
408+
INodeDefManager *ndef)
409409
{
410410
MapNode *orig_schemdata = schemdata;
411411
std::vector<std::string> ndef_nodenames;
412412
std::vector<std::string> *names;
413413

414-
// Only carry out the modification if we know the nodes
415-
// were resolved at this point
416-
if (m_resolve_done) {
414+
if (m_resolve_done && ndef == NULL)
415+
ndef = m_ndef;
416+
417+
if (ndef) {
417418
names = &ndef_nodenames;
418419

419420
u32 volume = size.X * size.Y * size.Z;
420421
schemdata = new MapNode[volume];
421422
for (u32 i = 0; i != volume; i++)
422423
schemdata[i] = orig_schemdata[i];
423424

424-
generate_nodelist_and_update_ids(schemdata, volume, names, m_ndef);
425+
generate_nodelist_and_update_ids(schemdata, volume, names, ndef);
425426
} else { // otherwise, use the names we have on hand in the list
426427
names = &m_nodenames;
427428
}
428429

429430
std::ostringstream os(std::ios_base::binary);
430-
serializeToMts(&os, *names);
431+
bool status = serializeToMts(&os, *names);
431432

432-
if (m_resolve_done) {
433+
if (ndef) {
433434
delete []schemdata;
434435
schemdata = orig_schemdata;
435436
}
436437

438+
if (!status)
439+
return false;
440+
437441
return fs::safeWriteToFile(filename, os.str());
438442
}
439443

‎src/mg_schematic.h

+11-16
Original file line numberDiff line numberDiff line change
@@ -85,38 +85,33 @@ enum SchematicFormatType {
8585

8686
class Schematic : public ObjDef, public NodeResolver {
8787
public:
88-
std::vector<content_t> c_nodes;
89-
90-
u32 flags;
91-
v3s16 size;
92-
MapNode *schemdata;
93-
u8 *slice_probs;
94-
9588
Schematic();
9689
virtual ~Schematic();
9790

9891
virtual void resolveNodeNames();
9992

100-
void updateContentIds();
101-
102-
void blitToVManip(v3s16 p, MMVManip *vm,
103-
Rotation rot, bool force_placement);
104-
10593
bool loadSchematicFromFile(const std::string &filename, INodeDefManager *ndef,
106-
StringMap *replace_names);
107-
bool saveSchematicToFile(const std::string &filename);
94+
StringMap *replace_names=NULL);
95+
bool saveSchematicToFile(const std::string &filename, INodeDefManager *ndef);
10896
bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2);
10997

11098
bool deserializeFromMts(std::istream *is, std::vector<std::string> *names);
11199
bool serializeToMts(std::ostream *os, const std::vector<std::string> &names);
112100
bool serializeToLua(std::ostream *os, const std::vector<std::string> &names,
113101
bool use_comments, u32 indent_spaces);
114102

115-
void placeStructure(Map *map, v3s16 p, u32 flags,
116-
Rotation rot, bool force_placement);
103+
void blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_place);
104+
void placeStructure(Map *map, v3s16 p, u32 flags, Rotation rot, bool force_place);
105+
117106
void applyProbabilities(v3s16 p0,
118107
std::vector<std::pair<v3s16, u8> > *plist,
119108
std::vector<std::pair<s16, u8> > *splist);
109+
110+
std::vector<content_t> c_nodes;
111+
u32 flags;
112+
v3s16 size;
113+
MapNode *schemdata;
114+
u8 *slice_probs;
120115
};
121116

122117
class SchematicManager : public ObjDefManager {

‎src/script/lua_api/l_mapgen.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -1031,10 +1031,9 @@ int ModApiMapgen::l_generate_decorations(lua_State *L)
10311031
// create_schematic(p1, p2, probability_list, filename, y_slice_prob_list)
10321032
int ModApiMapgen::l_create_schematic(lua_State *L)
10331033
{
1034-
Schematic schem;
1035-
schem.m_ndef = getServer(L)->getNodeDefManager();
1036-
1034+
INodeDefManager *ndef = getServer(L)->getNodeDefManager();
10371035
Map *map = &(getEnv(L)->getMap());
1036+
Schematic schem;
10381037

10391038
v3s16 p1 = check_v3s16(L, 1);
10401039
v3s16 p2 = check_v3s16(L, 2);
@@ -1081,7 +1080,7 @@ int ModApiMapgen::l_create_schematic(lua_State *L)
10811080

10821081
schem.applyProbabilities(p1, &prob_list, &slice_prob_list);
10831082

1084-
schem.saveSchematicToFile(filename);
1083+
schem.saveSchematicToFile(filename, ndef);
10851084
actionstream << "create_schematic: saved schematic file '"
10861085
<< filename << "'." << std::endl;
10871086

‎src/unittest/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set (UNITTEST_SRCS
1212
${CMAKE_CURRENT_SOURCE_DIR}/test_objdef.cpp
1313
${CMAKE_CURRENT_SOURCE_DIR}/test_profiler.cpp
1414
${CMAKE_CURRENT_SOURCE_DIR}/test_random.cpp
15+
${CMAKE_CURRENT_SOURCE_DIR}/test_schematic.cpp
1516
${CMAKE_CURRENT_SOURCE_DIR}/test_serialization.cpp
1617
${CMAKE_CURRENT_SOURCE_DIR}/test_settings.cpp
1718
${CMAKE_CURRENT_SOURCE_DIR}/test_socket.cpp

‎src/unittest/test.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,35 @@ bool TestBase::testModule(IGameDef *gamedef)
276276
<< " failures / " << num_tests_run << " tests) - " << tdiff
277277
<< "ms" << std::endl;
278278

279+
if (!m_test_dir.empty())
280+
fs::RecursiveDelete(m_test_dir);
281+
279282
return num_tests_failed == 0;
280283
}
281284

285+
std::string TestBase::getTestTempDirectory()
286+
{
287+
if (!m_test_dir.empty())
288+
return m_test_dir;
289+
290+
char buf[32];
291+
snprintf(buf, sizeof(buf), "%08X", myrand());
292+
293+
m_test_dir = fs::TempPath() + DIR_DELIM "mttest_" + buf;
294+
if (!fs::CreateDir(m_test_dir))
295+
throw TestFailedException();
296+
297+
return m_test_dir;
298+
}
299+
300+
std::string TestBase::getTestTempFile()
301+
{
302+
char buf[32];
303+
snprintf(buf, sizeof(buf), "%08X", myrand());
304+
305+
return getTestTempDirectory() + DIR_DELIM + buf + ".tmp";
306+
}
307+
282308

283309
/*
284310
NOTE: These tests became non-working then NodeContainer was removed.

‎src/unittest/test.h

+6
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,17 @@ class IGameDef;
102102
class TestBase {
103103
public:
104104
bool testModule(IGameDef *gamedef);
105+
std::string getTestTempDirectory();
106+
std::string getTestTempFile();
107+
105108
virtual void runTests(IGameDef *gamedef) = 0;
106109
virtual const char *getName() = 0;
107110

108111
u32 num_tests_failed;
109112
u32 num_tests_run;
113+
114+
private:
115+
std::string m_test_dir;
110116
};
111117

112118
class TestManager {

‎src/unittest/test_schematic.cpp

+265
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
/*
2+
Minetest
3+
Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
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 "mg_schematic.h"
23+
#include "gamedef.h"
24+
#include "nodedef.h"
25+
26+
class TestSchematic : public TestBase {
27+
public:
28+
TestSchematic() { TestManager::registerTestModule(this); }
29+
const char *getName() { return "TestSchematic"; }
30+
31+
void runTests(IGameDef *gamedef);
32+
33+
void testMtsSerializeDeserialize(INodeDefManager *ndef);
34+
void testLuaTableSerialize(INodeDefManager *ndef);
35+
void testFileSerializeDeserialize(INodeDefManager *ndef);
36+
37+
static const content_t test_schem_data[7 * 6 * 4];
38+
static const content_t test_schem_data2[3 * 3 * 3];
39+
static const char *expected_lua_output;
40+
};
41+
42+
static TestSchematic g_test_instance;
43+
44+
void TestSchematic::runTests(IGameDef *gamedef)
45+
{
46+
IWritableNodeDefManager *ndef =
47+
(IWritableNodeDefManager *)gamedef->getNodeDefManager();
48+
49+
ndef->setNodeRegistrationStatus(true);
50+
51+
TEST(testMtsSerializeDeserialize, ndef);
52+
TEST(testLuaTableSerialize, ndef);
53+
TEST(testFileSerializeDeserialize, ndef);
54+
55+
ndef->resetNodeResolveState();
56+
}
57+
58+
////////////////////////////////////////////////////////////////////////////////
59+
60+
void TestSchematic::testMtsSerializeDeserialize(INodeDefManager *ndef)
61+
{
62+
static const v3s16 size(7, 6, 4);
63+
static const u32 volume = size.X * size.Y * size.Z;
64+
65+
std::stringstream ss(std::ios_base::binary |
66+
std::ios_base::in | std::ios_base::out);
67+
68+
std::vector<std::string> names;
69+
names.push_back("foo");
70+
names.push_back("bar");
71+
names.push_back("baz");
72+
names.push_back("qux");
73+
74+
Schematic schem, schem2;
75+
76+
schem.flags = 0;
77+
schem.size = size;
78+
schem.schemdata = new MapNode[volume];
79+
schem.slice_probs = new u8[size.Y];
80+
for (size_t i = 0; i != volume; i++)
81+
schem.schemdata[i] = MapNode(test_schem_data[i], MTSCHEM_PROB_ALWAYS, 0);
82+
for (size_t y = 0; y != size.Y; y++)
83+
schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;
84+
85+
UASSERT(schem.serializeToMts(&ss, names));
86+
87+
ss.seekg(0);
88+
names.clear();
89+
90+
UASSERT(schem2.deserializeFromMts(&ss, &names));
91+
92+
UASSERTEQ(size_t, names.size(), 4);
93+
UASSERTEQ(std::string, names[0], "foo");
94+
UASSERTEQ(std::string, names[1], "bar");
95+
UASSERTEQ(std::string, names[2], "baz");
96+
UASSERTEQ(std::string, names[3], "qux");
97+
98+
UASSERT(schem2.size == size);
99+
for (size_t i = 0; i != volume; i++)
100+
UASSERT(schem2.schemdata[i] == schem.schemdata[i]);
101+
for (size_t y = 0; y != size.Y; y++)
102+
UASSERTEQ(u8, schem2.slice_probs[y], schem.slice_probs[y]);
103+
}
104+
105+
106+
void TestSchematic::testLuaTableSerialize(INodeDefManager *ndef)
107+
{
108+
static const v3s16 size(3, 3, 3);
109+
static const u32 volume = size.X * size.Y * size.Z;
110+
111+
Schematic schem;
112+
113+
schem.flags = 0;
114+
schem.size = size;
115+
schem.schemdata = new MapNode[volume];
116+
schem.slice_probs = new u8[size.Y];
117+
for (size_t i = 0; i != volume; i++)
118+
schem.schemdata[i] = MapNode(test_schem_data2[i], MTSCHEM_PROB_ALWAYS, 0);
119+
for (size_t y = 0; y != size.Y; y++)
120+
schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;
121+
122+
std::vector<std::string> names;
123+
names.push_back("air");
124+
names.push_back("default:lava_source");
125+
names.push_back("default:glass");
126+
127+
std::ostringstream ss(std::ios_base::binary);
128+
129+
UASSERT(schem.serializeToLua(&ss, names, false, 0));
130+
UASSERTEQ(std::string, ss.str(), expected_lua_output);
131+
}
132+
133+
134+
void TestSchematic::testFileSerializeDeserialize(INodeDefManager *ndef)
135+
{
136+
static const v3s16 size(3, 3, 3);
137+
static const u32 volume = size.X * size.Y * size.Z;
138+
static const content_t content_map[] = {
139+
CONTENT_AIR,
140+
t_CONTENT_STONE,
141+
t_CONTENT_LAVA,
142+
};
143+
static const content_t content_map2[] = {
144+
CONTENT_AIR,
145+
t_CONTENT_STONE,
146+
t_CONTENT_WATER,
147+
};
148+
StringMap replace_names;
149+
replace_names["default:lava"] = "default:water";
150+
151+
Schematic schem1, schem2;
152+
153+
//// Construct the schematic to save
154+
schem1.flags = 0;
155+
schem1.size = size;
156+
schem1.schemdata = new MapNode[volume];
157+
schem1.slice_probs = new u8[size.Y];
158+
schem1.slice_probs[0] = 80;
159+
schem1.slice_probs[1] = 160;
160+
schem1.slice_probs[2] = 240;
161+
162+
for (size_t i = 0; i != volume; i++) {
163+
content_t c = content_map[test_schem_data2[i]];
164+
schem1.schemdata[i] = MapNode(c, MTSCHEM_PROB_ALWAYS, 0);
165+
}
166+
167+
std::string temp_file = getTestTempFile();
168+
UASSERT(schem1.saveSchematicToFile(temp_file, ndef));
169+
UASSERT(schem2.loadSchematicFromFile(temp_file, ndef, &replace_names));
170+
171+
UASSERT(schem2.size == size);
172+
UASSERT(schem2.slice_probs[0] == 80);
173+
UASSERT(schem2.slice_probs[1] == 160);
174+
UASSERT(schem2.slice_probs[2] == 240);
175+
176+
for (size_t i = 0; i != volume; i++) {
177+
content_t c = content_map2[test_schem_data2[i]];
178+
UASSERT(schem2.schemdata[i] == MapNode(c, MTSCHEM_PROB_ALWAYS, 0));
179+
}
180+
}
181+
182+
183+
// Should form a cross-shaped-thing...?
184+
const content_t TestSchematic::test_schem_data[7 * 6 * 4] = {
185+
3, 3, 1, 1, 1, 3, 3, // Y=0, Z=0
186+
3, 0, 1, 2, 1, 0, 3, // Y=1, Z=0
187+
3, 0, 1, 2, 1, 0, 3, // Y=2, Z=0
188+
3, 1, 1, 2, 1, 1, 3, // Y=3, Z=0
189+
3, 2, 2, 2, 2, 2, 3, // Y=4, Z=0
190+
3, 1, 1, 2, 1, 1, 3, // Y=5, Z=0
191+
192+
0, 0, 1, 1, 1, 0, 0, // Y=0, Z=1
193+
0, 0, 1, 2, 1, 0, 0, // Y=1, Z=1
194+
0, 0, 1, 2, 1, 0, 0, // Y=2, Z=1
195+
1, 1, 1, 2, 1, 1, 1, // Y=3, Z=1
196+
1, 2, 2, 2, 2, 2, 1, // Y=4, Z=1
197+
1, 1, 1, 2, 1, 1, 1, // Y=5, Z=1
198+
199+
0, 0, 1, 1, 1, 0, 0, // Y=0, Z=2
200+
0, 0, 1, 2, 1, 0, 0, // Y=1, Z=2
201+
0, 0, 1, 2, 1, 0, 0, // Y=2, Z=2
202+
1, 1, 1, 2, 1, 1, 1, // Y=3, Z=2
203+
1, 2, 2, 2, 2, 2, 1, // Y=4, Z=2
204+
1, 1, 1, 2, 1, 1, 1, // Y=5, Z=2
205+
206+
3, 3, 1, 1, 1, 3, 3, // Y=0, Z=3
207+
3, 0, 1, 2, 1, 0, 3, // Y=1, Z=3
208+
3, 0, 1, 2, 1, 0, 3, // Y=2, Z=3
209+
3, 1, 1, 2, 1, 1, 3, // Y=3, Z=3
210+
3, 2, 2, 2, 2, 2, 3, // Y=4, Z=3
211+
3, 1, 1, 2, 1, 1, 3, // Y=5, Z=3
212+
};
213+
214+
const content_t TestSchematic::test_schem_data2[3 * 3 * 3] = {
215+
0, 0, 0,
216+
0, 2, 0,
217+
0, 0, 0,
218+
219+
0, 2, 0,
220+
2, 1, 2,
221+
0, 2, 0,
222+
223+
0, 0, 0,
224+
0, 2, 0,
225+
0, 0, 0,
226+
};
227+
228+
const char *TestSchematic::expected_lua_output =
229+
"schematic = {\n"
230+
"\tsize = {x=3, y=3, z=3},\n"
231+
"\tyslice_prob = {\n"
232+
"\t\t{ypos=0, prob=255},\n"
233+
"\t\t{ypos=1, prob=255},\n"
234+
"\t\t{ypos=2, prob=255},\n"
235+
"\t},\n"
236+
"\tdata = {\n"
237+
"\t\t{name=\"air\", param1=255, param2=0},\n"
238+
"\t\t{name=\"air\", param1=255, param2=0},\n"
239+
"\t\t{name=\"air\", param1=255, param2=0},\n"
240+
"\t\t{name=\"air\", param1=255, param2=0},\n"
241+
"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
242+
"\t\t{name=\"air\", param1=255, param2=0},\n"
243+
"\t\t{name=\"air\", param1=255, param2=0},\n"
244+
"\t\t{name=\"air\", param1=255, param2=0},\n"
245+
"\t\t{name=\"air\", param1=255, param2=0},\n"
246+
"\t\t{name=\"air\", param1=255, param2=0},\n"
247+
"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
248+
"\t\t{name=\"air\", param1=255, param2=0},\n"
249+
"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
250+
"\t\t{name=\"default:lava_source\", param1=255, param2=0},\n"
251+
"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
252+
"\t\t{name=\"air\", param1=255, param2=0},\n"
253+
"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
254+
"\t\t{name=\"air\", param1=255, param2=0},\n"
255+
"\t\t{name=\"air\", param1=255, param2=0},\n"
256+
"\t\t{name=\"air\", param1=255, param2=0},\n"
257+
"\t\t{name=\"air\", param1=255, param2=0},\n"
258+
"\t\t{name=\"air\", param1=255, param2=0},\n"
259+
"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
260+
"\t\t{name=\"air\", param1=255, param2=0},\n"
261+
"\t\t{name=\"air\", param1=255, param2=0},\n"
262+
"\t\t{name=\"air\", param1=255, param2=0},\n"
263+
"\t\t{name=\"air\", param1=255, param2=0},\n"
264+
"\t},\n"
265+
"}\n";

0 commit comments

Comments
 (0)
Please sign in to comment.