1 /**
2  * @file
3  * @brief Test cases for checking the scripts for logic errors
4  */
5 
6 /*
7 Copyright (C) 2002-2013 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 */
25 
26 #include "test_shared.h"
27 #include "test_scripts.h"
28 #include "../client/client.h"
29 #include "../client/renderer/r_state.h" /* r_state */
30 #include "../client/ui/ui_main.h"
31 #include "../client/battlescape/cl_particle.h"
32 #include "../client/cgame/campaign/cp_campaign.h"
33 
34 /**
35  * The suite initialization function.
36  * Returns zero on success, non-zero otherwise.
37  */
UFO_InitSuiteScripts(void)38 static int UFO_InitSuiteScripts (void)
39 {
40 	TEST_Init();
41 
42 	cl_genericPool = Mem_CreatePool("Client: Generic");
43 	vid_imagePool = Mem_CreatePool("Vid: Image system");
44 
45 	r_state.active_texunit = &r_state.texunits[0];
46 	R_FontInit();
47 	UI_Init();
48 
49 	OBJZERO(cls);
50 	Com_ParseScripts(false);
51 
52 	return 0;
53 }
54 
55 /**
56  * The suite cleanup function.
57  * Returns zero on success, non-zero otherwise.
58  */
UFO_CleanSuiteScripts(void)59 static int UFO_CleanSuiteScripts (void)
60 {
61 	UI_Shutdown();
62 	TEST_Shutdown();
63 	return 0;
64 }
65 
TEST_CheckImage(const char * path)66 static bool TEST_CheckImage (const char* path)
67 {
68 	const char* extensions[] = {"png", "tga", "jpg", nullptr};
69 	int i = 0;
70 
71 	if (Q_strnull(path))
72 		return true;
73 
74 	while (extensions[i]) {
75 		if (FS_CheckFile("pics/%s.%s", path, extensions[i]) != -1)
76 			return true;
77 		i++;
78 	}
79 
80 	return false;
81 }
82 
TEST_CheckModel(const char * path)83 static bool TEST_CheckModel (const char* path)
84 {
85 	const char* extensions[] = {"md2", "md3", "obj", nullptr};
86 	int i = 0;
87 	if (Q_strnull(path))
88 		return true;
89 
90 	while (extensions[i]) {
91 		if (FS_CheckFile("models/%s.%s", path, extensions[i]) != -1)
92 			return true;
93 		i++;
94 	}
95 
96 	return false;
97 }
98 
TEST_CheckSound(const char * path)99 static bool TEST_CheckSound (const char* path)
100 {
101 	const char* extensions[] = {"wav", "ogg", nullptr};
102 	int i = 0;
103 
104 	if (Q_strnull(path))
105 		return true;
106 
107 	while (extensions[i]) {
108 		if (FS_CheckFile("sound/%s.%s", path, extensions[i]) != -1)
109 			return true;
110 		i++;
111 	}
112 
113 	return false;
114 }
115 
TEST_CheckParticle(const char * particleID)116 static bool TEST_CheckParticle (const char* particleID)
117 {
118 	if (Q_strnull(particleID))
119 		return true;
120 
121 	/* find the particle definition */
122 	return CL_ParticleGet(particleID) != nullptr;
123 }
124 
testTeamDefs(void)125 static void testTeamDefs (void)
126 {
127 	int i;
128 
129 	for (i = 0; i < csi.numTeamDefs; i++) {
130 		const teamDef_t* teamDef = &csi.teamDef[i];
131 		int k;
132 
133 		UFO_CU_ASSERT_TRUE_MSG(teamDef->numTemplates > 0, va("%s has no character templates assigned", teamDef->id));
134 
135 		for (k = 0; k < SND_MAX; k++) {
136 			int l;
137 			for (l = 0; l < NAME_LAST; l++) {
138 				LIST_Foreach(teamDef->sounds[k][l], char, soundFile) {
139 					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckSound(soundFile), va("sound %s does not exist (team %s)", soundFile, teamDef->id));
140 				}
141 			}
142 		}
143 	}
144 }
145 
testTeamDefsModelScriptData(void)146 static void testTeamDefsModelScriptData (void)
147 {
148 	int i;
149 	linkedList_t* armourPaths = nullptr;
150 
151 	for (i = 0; i < csi.numTeamDefs; i++) {
152 		int j;
153 		const teamDef_t* teamDef = &csi.teamDef[i];
154 		if (!teamDef->armour)
155 			continue;
156 
157 		for (j = 0; j < csi.numODs; j++) {
158 			const objDef_t* od = INVSH_GetItemByIDX(j);
159 			if (!od->isArmour())
160 				continue;
161 
162 			/* not for this team */
163 			if (!CHRSH_IsArmourUseableForTeam(od, teamDef))
164 				continue;
165 
166 			if (!LIST_ContainsString(armourPaths, od->armourPath))
167 				LIST_AddString(&armourPaths, od->armourPath);
168 		}
169 
170 		UFO_CU_ASSERT_TRUE_MSG(!LIST_IsEmpty(armourPaths), va("no armour definitions found for team %s - but armour is set to true",
171 				teamDef->id));
172 
173 		LIST_Foreach(armourPaths, char const, armourPath) {
174 			int l;
175 
176 			for (l = NAME_NEUTRAL; l < NAME_LAST; l++) {
177 				/* no models for this gender */
178 				if (!teamDef->numModels[l])
179 					continue;
180 
181 				CU_ASSERT_PTR_NOT_NULL_FATAL(teamDef->models[l]);
182 
183 				for (linkedList_t const* list = teamDef->models[l]; list; list = list->next) {
184 					teamDef_t::model_t const& m = *static_cast<teamDef_t::model_t const*>(list->data);
185 
186 					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", m.path, m.body)), va("%s does not exist in models/%s (teamDef: %s)",
187 							m.body, m.path, teamDef->id));
188 					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", m.path, armourPath, m.body)), va("%s does not exist in models/%s%s (teamDef: %s)",
189 							m.body, m.path, armourPath, teamDef->id));
190 
191 					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", m.path, m.head)), va("%s does not exist in models/%s (teamDef: %s)",
192 							m.head, m.path, teamDef->id));
193 					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", m.path, armourPath, m.head)), va("%s does not exist in models/%s%s (teamDef: %s)",
194 							m.head, m.path, armourPath, teamDef->id));
195 				}
196 			}
197 		}
198 
199 		LIST_Delete(&armourPaths);
200 	}
201 }
202 
testItems(void)203 static void testItems (void)
204 {
205 	int j;
206 
207 	for (j = 0; j < csi.numODs; j++) {
208 		const objDef_t* od = INVSH_GetItemByIDX(j);
209 		int i;
210 
211 		if (od->isVirtual || od->isDummy)
212 			continue;
213 
214 		UFO_CU_ASSERT_TRUE_MSG(TEST_CheckSound(od->reloadSound), va("sound %s does not exist (item %s)", od->reloadSound, od->id));
215 		UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(od->model), va("model %s does not exist (item %s)", od->model, od->id));
216 		UFO_CU_ASSERT_TRUE_MSG(TEST_CheckImage(od->image), va("image %s does not exist (item %s)", od->image, od->id));
217 
218 		for (i = 0; i < od->numWeapons; i++) {
219 			int k;
220 
221 			for (k = 0; k < od->numFiredefs[i]; k++) {
222 				const fireDef_t* fd = &od->fd[i][k];
223 				UFO_CU_ASSERT_TRUE_MSG(TEST_CheckSound(fd->bounceSound), va("sound %s does not exist (firedef %s for item %s)", fd->bounceSound, fd->name, od->id));
224 				UFO_CU_ASSERT_TRUE_MSG(TEST_CheckSound(fd->fireSound), va("sound %s does not exist (firedef %s for item %s)", fd->fireSound, fd->name, od->id));
225 				UFO_CU_ASSERT_TRUE_MSG(TEST_CheckSound(fd->impactSound), va("sound %s does not exist (firedef %s for item %s)", fd->impactSound, fd->name, od->id));
226 				UFO_CU_ASSERT_TRUE_MSG(TEST_CheckSound(fd->hitBodySound), va("sound %s does not exist (firedef %s for item %s)", fd->hitBodySound, fd->name, od->id));
227 				UFO_CU_ASSERT_TRUE_MSG(TEST_CheckParticle(fd->hitBody), va("particle %s does not exist (firedef %s for item %s)", fd->hitBody, fd->name, od->id));
228 				UFO_CU_ASSERT_TRUE_MSG(TEST_CheckParticle(fd->impact), va("particle %s does not exist (firedef %s for item %s)", fd->impact, fd->name, od->id));
229 				UFO_CU_ASSERT_TRUE_MSG(TEST_CheckParticle(fd->projectile), va("particle %s does not exist (firedef %s for item %s)", fd->projectile, fd->name, od->id));
230 			}
231 		}
232 	}
233 }
234 
testNations(void)235 static void testNations (void)
236 {
237 	int i;
238 
239 	for (i = 0; i < ccs.numNations; i++) {
240 		const nation_t* nat = NAT_GetNationByIDX(i);
241 		UFO_CU_ASSERT_TRUE_MSG(TEST_CheckImage(va("nations/%s", nat->id)), va("nation %s has no image", nat->id));
242 		CU_ASSERT_PTR_NOT_NULL(Com_GetTeamDefinitionByID(nat->id));
243 	}
244 }
245 
testAircraft(void)246 static void testAircraft (void)
247 {
248 	AIR_Foreach(aircraft) {
249 		UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(aircraft->model), va("%s does not exist (aircraft: %s)", aircraft->model, aircraft->id));
250 		UFO_CU_ASSERT_TRUE_MSG(TEST_CheckImage(aircraft->image), va("%s does not exist (aircraft: %s)", aircraft->image, aircraft->id));
251 	}
252 }
253 
testMapDef(void)254 static void testMapDef (void)
255 {
256 	int i;
257 	const mapDef_t* md;
258 
259 	i = 0;
260 	MapDef_Foreach(md) {
261 		if (md->civTeam != nullptr)
262 			CU_ASSERT_PTR_NOT_NULL(Com_GetTeamDefinitionByID(md->civTeam));
263 
264 		CU_ASSERT_FALSE(md->maxAliens <= 0);
265 		CU_ASSERT_PTR_NOT_NULL(md->map);
266 		CU_ASSERT_PTR_NOT_NULL(md->description);
267 		i++;
268 	}
269 
270 	CU_ASSERT_PTR_NULL(md);
271 	UFO_CU_ASSERT_EQUAL_INT_MSG_FATAL(i, csi.numMDs, va("only looped over %i mapdefs, but expected %i", i, csi.numMDs));
272 
273 	i = csi.numMDs;
274 
275 	MapDef_ForeachCondition(md, !md->singleplayer || !md->campaign) {
276 		i--;
277 		CU_ASSERT_PTR_NOT_NULL(md);
278 	}
279 
280 	CU_ASSERT_PTR_NULL(md);
281 	CU_ASSERT_NOT_EQUAL(i, csi.numMDs);
282 
283 	MapDef_ForeachSingleplayerCampaign(md) {
284 		i--;
285 		CU_ASSERT_PTR_NOT_NULL(md);
286 		CU_ASSERT_STRING_NOT_EQUAL(md->id, "training_a");
287 		CU_ASSERT_STRING_NOT_EQUAL(md->id, "training_b");
288 	}
289 
290 	CU_ASSERT_PTR_NULL(md);
291 	UFO_CU_ASSERT_EQUAL_INT_MSG_FATAL(i, 0, va("i: %i", i));
292 }
293 
UFO_AddScriptsTests(void)294 int UFO_AddScriptsTests (void)
295 {
296 	/* add a suite to the registry */
297 	CU_pSuite ScriptsSuite = CU_add_suite("ScriptsTests", UFO_InitSuiteScripts, UFO_CleanSuiteScripts);
298 
299 	if (ScriptsSuite == nullptr)
300 		return CU_get_error();
301 
302 	/* add the tests to the suite */
303 	if (CU_ADD_TEST(ScriptsSuite, testTeamDefs) == nullptr)
304 		return CU_get_error();
305 
306 	if (CU_ADD_TEST(ScriptsSuite, testTeamDefsModelScriptData) == nullptr)
307 		return CU_get_error();
308 
309 	if (CU_ADD_TEST(ScriptsSuite, testItems) == nullptr)
310 		return CU_get_error();
311 
312 	if (CU_ADD_TEST(ScriptsSuite, testNations) == nullptr)
313 		return CU_get_error();
314 
315 	if (CU_ADD_TEST(ScriptsSuite, testAircraft) == nullptr)
316 		return CU_get_error();
317 
318 	if (CU_ADD_TEST(ScriptsSuite, testMapDef) == nullptr)
319 		return CU_get_error();
320 
321 	return CUE_SUCCESS;
322 }
323