Add unit tests for scripts. Add lint program for qtscript files for easier validation testing while writing scripts.

master
Per Inge Mathisen 2011-05-15 11:32:53 +02:00
parent 66c3bdb559
commit 9d0b310296
7 changed files with 736 additions and 7 deletions

View File

@ -1,20 +1,28 @@
AM_CPPFLAGS = $(SDL_CFLAGS) $(PHYSFS_CFLAGS) $(PNG_CFLAGS) $(OPENGL_CFLAGS) $(WZ_CPPFLAGS) -I$(top_srcdir)/tools -I. -I$(top_srcdir)/lib/framework
AM_CPPFLAGS = $(SDL_CFLAGS) $(PHYSFS_CFLAGS) $(PNG_CFLAGS) $(OPENGL_CFLAGS) $(WZ_CPPFLAGS) -I$(top_srcdir)/tools -I. -I$(top_srcdir)/lib/framework $(QT4_CFLAGS)
AM_CFLAGS = -g
BUILT_SOURCES = maplist.txt modellist.txt
BUILT_SOURCES = maplist.txt modellist.txt jslist.txt
check_PROGRAMS = maptest modeltest
bin_PROGRAMS = qslint
check_PROGRAMS = maptest modeltest qtscripttest
qslint_SOURCES = qslint.cpp lint.cpp
qslint_LDADD = $(PHYSFS_LIBS) $(QT4_LIBS)
qtscripttest_SOURCES = qtscripttest.cpp lint.cpp
qtscripttest_LDADD = $(PHYSFS_LIBS) $(QT4_LIBS)
modeltest_SOURCES = modeltest.c
maptest_SOURCES = ../tools/map/mapload.c maptest.c
noinst_HEADERS = ../tools/map/mapload.h
maptest_LDADD = $(PHYSFS_LIBS) $(PNG_LIBS)
noinst_HEADERS = ../tools/map/mapload.h lint.h
CLEANFILES = \
$(BUILT_SOURCES)
TESTS = maptest modeltest
TESTS = maptest modeltest qtscripttest
maplist.txt:
(cd $(abs_top_srcdir)/data ; find base mods -name game.map > $(abs_top_builddir)/tests/maplist.txt )
@ -23,3 +31,7 @@ maplist.txt:
modellist.txt:
(cd $(abs_top_srcdir)/data ; find base mods mp -iname \*.pie > $(abs_top_builddir)/tests/modellist.txt )
touch $@
jslist.txt:
(cd $(abs_top_srcdir)/data ; find base mods mp -name \*.js > $(abs_top_builddir)/tests/jslist.txt )
touch $@

636
tests/lint.cpp Normal file
View File

@ -0,0 +1,636 @@
/*
This file is part of Warzone 2100.
Copyright (C) 2011 Warzone 2100 Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file lint.cpp
*
* New scripting system tester
*/
#include "lint.h"
#include <QtCore/QTextStream>
#include <QtCore/QHash>
#include <QtScript/QScriptEngine>
#include <QtScript/QScriptValue>
#include <QtScript/QScriptValueIterator>
#include <QtScript/QScriptSyntaxCheckResult>
#include <QtCore/QList>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QSettings>
#include <QtCore/QFile>
// TODO -- these should go into a common header
enum SCRIPT_TRIGGER_TYPE
{
TRIGGER_GAME_INIT
};
#define CAMP_CLEAN 0 // campaign subtypes
#define CAMP_BASE 1
#define CAMP_WALLS 2
#define NO_ALLIANCES 0 //alliance possibilities for games.
#define ALLIANCES 1
#define ALLIANCES_TEAMS 2 //locked teams
#define LEV_LOW 400 // how many points to allocate for res levels???
#define LEV_MED 700
#define LEV_HI 1000
struct timerNode
{
QString function;
QScriptEngine *engine;
int player;
timerNode() {}
timerNode(QScriptEngine *caller, QString val, int plr) : function(val) { player = plr; engine = caller; }
bool operator== (const timerNode &t) { return function == t.function && player == t.player; }
};
static QList<timerNode> timers;
// Pseudorandom values
static int obj_uid = 11;
#define MAX_PLAYERS 8
// ----------------------------------------------------------------------------------------
// Utility functions -- not called directly from scripts
#define SCRIPT_ASSERT(context, expr, ...) \
do { bool _wzeval = (expr); if (!_wzeval) { qWarning(__VA_ARGS__); context->throwError(QScriptContext::ReferenceError, QString(#expr) + " failed in " + QString(__FUNCTION__) + " at line " + QString::number(__LINE__)); return QScriptValue(); } } while (0)
#define ARG_NUMBER(vnum) \
SCRIPT_ASSERT(context, context->argument(vnum).isNumber(), "Argument %d should be a number", vnum)
#define ARG_STRING(vnum) \
SCRIPT_ASSERT(context, context->argument(vnum).isString(), "Argument %d should be a string", vnum)
#define ARG_BOOL(vnum) \
SCRIPT_ASSERT(context, context->argument(vnum).isBool(), "Argument %d should be a boolean", vnum)
#define ARG_COUNT_EXACT(vnum) \
SCRIPT_ASSERT(context, context->argumentCount() == vnum, "Wrong number of arguments - must be exactly %d", vnum);
#define ARG_COUNT_VAR(vmin, vmax) \
SCRIPT_ASSERT(context, context->argumentCount() >= vmin && context->argumentCount() <= vmax, "Wrong number of arguments - must be between %d and %d", vmin, vmax);
#define ARG_PLAYER(vnum) \
do { ARG_NUMBER(vnum); int vplayer = context->argument(vnum).toInt32(); SCRIPT_ASSERT(context, vplayer < MAX_PLAYERS && vplayer >= 0, "Invalid player %d", vplayer); } while(0)
#define ARG_OBJ(vnum) do { \
SCRIPT_ASSERT(context, context->argument(vnum).isObject(), "Argument %d should be an object", vnum); \
QScriptValue vval = context->argument(vnum); \
SCRIPT_ASSERT(context, vval.property("id").isNumber(), "Invalid object ID argument %d", vnum); \
SCRIPT_ASSERT(context, vval.property("player").isNumber(), "Invalid object player argument %d", vnum); \
int vplayer = vval.property("player").toInt32(); \
SCRIPT_ASSERT(context, vplayer < MAX_PLAYERS && vplayer >= 0, "Invalid object player %d", vplayer); \
} while(0)
#define ARG_DROID(vnum) ARG_OBJ(vnum)
#define ARG_STRUCT(vnum) ARG_OBJ(vnum)
// These functions invent new imaginary objects
QScriptValue convStructure(QScriptEngine *engine)
{
QScriptValue value = engine->newObject();
value.setProperty("id", obj_uid++, QScriptValue::ReadOnly);
value.setProperty("x", 11, QScriptValue::ReadOnly);
value.setProperty("y", 11, QScriptValue::ReadOnly);
value.setProperty("player", 1, QScriptValue::ReadOnly);
return value;
}
QScriptValue convDroid(QScriptEngine *engine)
{
QScriptValue value = engine->newObject();
value.setProperty("id", obj_uid++, QScriptValue::ReadOnly);
value.setProperty("x", 11, QScriptValue::ReadOnly);
value.setProperty("y", 11, QScriptValue::ReadOnly);
value.setProperty("player", 1, QScriptValue::ReadOnly);
return value;
}
QScriptValue convObj(QScriptEngine *engine)
{
QScriptValue value = engine->newObject();
value.setProperty("id", obj_uid++, QScriptValue::ReadOnly);
value.setProperty("x", 11, QScriptValue::ReadOnly);
value.setProperty("y", 11, QScriptValue::ReadOnly);
value.setProperty("player", 1, QScriptValue::ReadOnly);
return value;
}
// Call a function by name
static bool callFunction(QScriptEngine *engine, QString function, QScriptValueList args)
{
QScriptValue value = engine->globalObject().property(function);
if (!value.isValid() || !value.isFunction())
{
return false; // not necessarily an error, may just be a trigger that is not defined (ie not needed)
}
QScriptValue result = value.call(QScriptValue(), args);
if (engine->hasUncaughtException())
{
int line = engine->uncaughtExceptionLineNumber();
QStringList bt = engine->uncaughtExceptionBacktrace();
for (int i = 0; i < bt.size(); i++)
{
fprintf(stderr, "%d : %s\n", i, bt.at(i).toAscii().constData());
}
fprintf(stderr, "Uncaught exception calling function \"%s\" at line %d: %s\n",
function.toAscii().constData(), line, result.toString().toAscii().constData());
return false;
}
return true;
}
static QScriptValue js_enumGroup(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(1);
ARG_NUMBER(0);
QScriptValue result = engine->newArray(3);
for (int i = 0; i < 3; i++)
{
result.setProperty(i, convDroid(engine));
}
return result;
}
static QScriptValue js_newGroup(QScriptContext *context, QScriptEngine *engine)
{
return QScriptValue(1);
}
static QScriptValue js_enumStruct(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_VAR(0, 3);
switch (context->argumentCount())
{
default:
case 3: ARG_PLAYER(2); // fall-through
case 2: ARG_STRING(1); // fall-through
case 1: ARG_PLAYER(0); break;
}
QScriptValue result = engine->newArray(3);
for (int i = 0; i < 3; i++)
{
result.setProperty(i, convStructure(engine));
}
return result;
}
static QScriptValue js_enumDroid(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_VAR(0, 3);
switch (context->argumentCount())
{
default:
case 3: ARG_PLAYER(2); // fall-through
case 2: ARG_NUMBER(1); // fall-through
case 1: ARG_PLAYER(0); break;
}
QScriptValue result = engine->newArray(3);
for (int i = 0; i < 3; i++)
{
result.setProperty(i, convDroid(engine));
}
return result;
}
static QScriptValue js_debug(QScriptContext *context, QScriptEngine *engine)
{
return QScriptValue();
}
static QScriptValue js_structureIdle(QScriptContext *context, QScriptEngine *)
{
ARG_COUNT_EXACT(1);
ARG_STRUCT(0);
return QScriptValue(true);
}
static QScriptValue js_console(QScriptContext *context, QScriptEngine *engine)
{
return QScriptValue();
}
/* Build a droid template in the specified factory */
static QScriptValue js_buildDroid(QScriptContext *context, QScriptEngine *)
{
ARG_COUNT_EXACT(2);
ARG_STRING(0);
ARG_STRUCT(1);
return QScriptValue(true);
}
static QScriptValue js_groupAddArea(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(5);
ARG_NUMBER(0);
ARG_NUMBER(1);
ARG_NUMBER(2);
ARG_NUMBER(3);
ARG_NUMBER(4);
return QScriptValue();
}
static QScriptValue js_groupAddDroid(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(2);
ARG_NUMBER(0);
ARG_DROID(1);
return QScriptValue();
}
static QScriptValue js_distBetweenTwoPoints(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(4);
ARG_NUMBER(0);
ARG_NUMBER(1);
ARG_NUMBER(2);
ARG_NUMBER(3);
return QScriptValue(10);
}
static QScriptValue js_groupSize(QScriptContext *context, QScriptEngine *)
{
ARG_COUNT_EXACT(1);
ARG_NUMBER(0);
return QScriptValue(3);
}
static QScriptValue js_orderDroidLoc(QScriptContext *context, QScriptEngine *)
{
ARG_COUNT_EXACT(4);
ARG_DROID(0);
ARG_NUMBER(1);
ARG_NUMBER(2);
ARG_NUMBER(3);
return QScriptValue();
}
static QScriptValue js_setMissionTime(QScriptContext *context, QScriptEngine *)
{
ARG_COUNT_EXACT(1);
ARG_NUMBER(0);
return QScriptValue();
}
static QScriptValue js_setReinforcementTime(QScriptContext *context, QScriptEngine *)
{
ARG_COUNT_EXACT(1);
ARG_NUMBER(0);
return QScriptValue();
}
static QScriptValue js_setStructureLimits(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_VAR(2, 3);
ARG_STRING(0);
ARG_NUMBER(1);
if (context->argumentCount() > 2)
{
ARG_PLAYER(2);
}
return QScriptValue();
}
static QScriptValue js_centreView(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(2);
ARG_NUMBER(0);
ARG_NUMBER(1);
return QScriptValue();
}
static QScriptValue js_playSound(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_VAR(1, 4);
ARG_NUMBER(0);
if (context->argumentCount() != 1)
{
SCRIPT_ASSERT(context, context->argumentCount() == 4, "Arguments must be either 1 or 4");
ARG_NUMBER(1);
ARG_NUMBER(2);
ARG_NUMBER(3);
}
return QScriptValue();
}
static QScriptValue js_gameOverMessage(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(1);
ARG_BOOL(0);
return QScriptValue();
}
static QScriptValue js_completeResearch(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_VAR(1, 2);
ARG_STRING(0);
if (context->argumentCount() > 1)
{
ARG_PLAYER(1);
}
return QScriptValue();
}
static QScriptValue js_enableResearch(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_VAR(1, 2);
ARG_STRING(0);
if (context->argumentCount() > 1)
{
ARG_PLAYER(1);
}
return QScriptValue();
}
static QScriptValue js_setPower(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_VAR(1, 2);
ARG_NUMBER(0);
if (context->argumentCount() > 1)
{
ARG_PLAYER(1);
}
return QScriptValue();
}
static QScriptValue js_enableStructure(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_VAR(1, 2);
ARG_STRING(0);
if (context->argumentCount() > 1)
{
ARG_PLAYER(1);
}
return QScriptValue();
}
static QScriptValue js_addReticuleButton(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(1);
ARG_NUMBER(0);
return QScriptValue();
}
static QScriptValue js_applyLimitSet(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(0);
return QScriptValue();
}
static QScriptValue js_enableComponent(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(2);
ARG_STRING(0);
ARG_PLAYER(1);
return QScriptValue();
}
static QScriptValue js_makeComponentAvailable(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(2);
ARG_STRING(0);
ARG_PLAYER(1);
return QScriptValue();
}
static QScriptValue js_allianceExistsBetween(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(2);
ARG_PLAYER(0);
ARG_PLAYER(1);
return QScriptValue(false);
}
static QScriptValue js_removeTimer(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(1);
ARG_STRING(0);
QString function = context->argument(0).toString();
int i, size = timers.size();
for (i = 0; i < size; ++i)
{
timerNode node = timers.at(i);
if (node.function == function)
{
return QScriptValue();
}
}
QString warnName = function.left(15) + "...";
SCRIPT_ASSERT(context, false, "Did not find timer %s to remove", warnName.toAscii().constData());
return QScriptValue();
}
/// Special scripting function that registers a non-specific timer event. Note: Functions must be passed
/// quoted, otherwise they will be inlined!
static QScriptValue js_setGlobalTimer(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(2);
ARG_STRING(0);
ARG_NUMBER(1);
QString funcName = context->argument(0).toString();
// TODO - check that a function by that name exists
int player = engine->globalObject().property("me").toInt32();
timerNode node(engine, funcName, player);
timers.push_back(node);
return QScriptValue();
}
/// Special scripting function that registers a object-specific timer event. Note: Functions must be passed
/// quoted, otherwise they will be inlined!
static QScriptValue js_setObjectTimer(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(2);
ARG_STRING(0);
ARG_NUMBER(1);
ARG_OBJ(2);
QString funcName = context->argument(0).toString();
// TODO - check that a function by that name exists
int player = engine->globalObject().property("me").toInt32();
timerNode node(engine, funcName, player);
timers.push_back(node);
return QScriptValue();
}
static QScriptValue js_include(QScriptContext *context, QScriptEngine *engine)
{
ARG_COUNT_EXACT(1);
ARG_STRING(0);
// TODO -- implement this somehow -- not sure how to handle paths here
#if 0
QString path = context->argument(0).toString();
UDWORD size;
char *bytes = NULL;
if (!loadFile(path.toAscii().constData(), &bytes, &size))
{
debug(LOG_ERROR, "Failed to read include file \"%s\"", path.toAscii().constData());
return false;
}
QString source = QString::fromAscii(bytes, size);
free(bytes);
QScriptSyntaxCheckResult syntax = QScriptEngine::checkSyntax(source);
if (syntax.state() != QScriptSyntaxCheckResult::Valid)
{
debug(LOG_ERROR, "Syntax error in include %s line %d: %s",
path.toAscii().constData(), syntax.errorLineNumber(), syntax.errorMessage().toAscii().constData());
return false;
}
context->setActivationObject(engine->globalObject());
context->setThisObject(engine->globalObject());
QScriptValue result = engine->evaluate(source, path);
if (engine->hasUncaughtException())
{
int line = engine->uncaughtExceptionLineNumber();
debug(LOG_ERROR, "Uncaught exception at line %d, include file %s: %s",
line, path.toAscii().constData(), result.toString().toAscii().constData());
return false;
}
#endif
return QScriptValue();
}
bool testPlayerScript(QString path, int player, int difficulty)
{
QScriptEngine *engine = new QScriptEngine();
QFile input(path);
input.open(QIODevice::ReadOnly);
QString source(QString::fromUtf8(input.readAll()));
input.close();
QScriptSyntaxCheckResult syntax = QScriptEngine::checkSyntax(source);
if (syntax.state() != QScriptSyntaxCheckResult::Valid)
{
fprintf(stderr, "Syntax error in %s line %d: %s\n", path.toAscii().constData(), syntax.errorLineNumber(), syntax.errorMessage().toAscii().constData());
return false;
}
QScriptValue result = engine->evaluate(source, path);
if (engine->hasUncaughtException())
{
int line = engine->uncaughtExceptionLineNumber();
fprintf(stderr, "Uncaught exception at line %d, file %s: %s\n", line, path.toAscii().constData(), result.toString().toAscii().constData());
return false;
}
// Special functions
engine->globalObject().setProperty("setGlobalTimer", engine->newFunction(js_setGlobalTimer));
engine->globalObject().setProperty("setObjectTimer", engine->newFunction(js_setObjectTimer));
engine->globalObject().setProperty("removeTimer", engine->newFunction(js_removeTimer));
engine->globalObject().setProperty("include", engine->newFunction(js_include));
// Special global variables
engine->globalObject().setProperty("me", player, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("gameTime", 2, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("difficulty", difficulty, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("mapName", "Test", QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("baseType", CAMP_BASE, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("alliancesType", ALLIANCES_TEAMS, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("powerType", LEV_MED, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("maxPlayers", 4, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("scavengers", true, QScriptValue::ReadOnly | QScriptValue::Undeletable);
// General functions -- geared for use in AI scripts
engine->globalObject().setProperty("debug", engine->newFunction(js_debug));
engine->globalObject().setProperty("console", engine->newFunction(js_console));
engine->globalObject().setProperty("structureIdle", engine->newFunction(js_structureIdle));
engine->globalObject().setProperty("buildDroid", engine->newFunction(js_buildDroid));
engine->globalObject().setProperty("enumStruct", engine->newFunction(js_enumStruct));
engine->globalObject().setProperty("enumDroid", engine->newFunction(js_enumDroid));
engine->globalObject().setProperty("enumGroup", engine->newFunction(js_enumGroup));
engine->globalObject().setProperty("distBetweenTwoPoints", engine->newFunction(js_distBetweenTwoPoints));
engine->globalObject().setProperty("newGroup", engine->newFunction(js_newGroup));
engine->globalObject().setProperty("groupAddArea", engine->newFunction(js_groupAddArea));
engine->globalObject().setProperty("groupAddDroid", engine->newFunction(js_groupAddDroid));
engine->globalObject().setProperty("groupSize", engine->newFunction(js_groupSize));
engine->globalObject().setProperty("orderDroidLoc", engine->newFunction(js_orderDroidLoc));
// Functions that operate on the current player only
engine->globalObject().setProperty("centreView", engine->newFunction(js_centreView));
engine->globalObject().setProperty("playSound", engine->newFunction(js_playSound));
engine->globalObject().setProperty("gameOverMessage", engine->newFunction(js_gameOverMessage));
// Global state manipulation -- not for use with skirmish AI (unless you want it to cheat, obviously)
engine->globalObject().setProperty("setStructureLimits", engine->newFunction(js_setStructureLimits));
engine->globalObject().setProperty("applyLimitSet", engine->newFunction(js_applyLimitSet));
engine->globalObject().setProperty("setMissionTime", engine->newFunction(js_setMissionTime));
engine->globalObject().setProperty("setReinforcementTime", engine->newFunction(js_setReinforcementTime));
engine->globalObject().setProperty("completeResearch", engine->newFunction(js_completeResearch));
engine->globalObject().setProperty("enableResearch", engine->newFunction(js_enableResearch));
engine->globalObject().setProperty("setPower", engine->newFunction(js_setPower));
engine->globalObject().setProperty("addReticuleButton", engine->newFunction(js_addReticuleButton));
engine->globalObject().setProperty("enableStructure", engine->newFunction(js_enableStructure));
engine->globalObject().setProperty("makeComponentAvailable", engine->newFunction(js_makeComponentAvailable));
engine->globalObject().setProperty("enableComponent", engine->newFunction(js_enableComponent));
engine->globalObject().setProperty("allianceExistsBetween", engine->newFunction(js_allianceExistsBetween));
// Set some useful constants
engine->globalObject().setProperty("DORDER_ATTACK", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("DORDER_MOVE", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("DORDER_SCOUT", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("DORDER_BUILD", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("mapWidth", 64, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("mapHeight", 64, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("COMMAND", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("OPTIONS", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("BUILD", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("MANUFACTURE", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("RESEARCH", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("INTELMAP", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("DESIGN", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("CANCEL", 0, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("CAMP_CLEAN", CAMP_CLEAN, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("CAMP_BASE", CAMP_BASE, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("CAMP_WALLS", CAMP_WALLS, QScriptValue::ReadOnly | QScriptValue::Undeletable);
// Call init
callFunction(engine, "eventGameInit", QScriptValueList());
// Now set gameTime to something proper
engine->globalObject().setProperty("gameTime", 10101, QScriptValue::ReadOnly | QScriptValue::Undeletable);
// Call other events
{
QScriptValueList args;
args += convDroid(engine);
args += convStructure(engine);
callFunction(engine, "eventDroidBuilt", args);
}
{
QScriptValueList args;
args += convStructure(engine);
args += convObj(engine);
callFunction(engine, "eventStructureAttacked", args);
}
// Now test timers
QMutableListIterator<timerNode> iter(timers);
while (iter.hasNext())
{
timerNode node = iter.next();
callFunction(node.engine, node.function, QScriptValueList());
}
// Clean up
delete engine;
timers.clear();
return true;
}
bool testGlobalScript(QString path)
{
return testPlayerScript(path, 0, 0);
}

29
tests/lint.h Normal file
View File

@ -0,0 +1,29 @@
/*
This file is part of Warzone 2100.
Copyright (C) 2011 Warzone 2100 Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __INCLUDED_LINT_H__
#define __INCLUDED_LINT_H__
#include <stdbool.h>
#include <QtCore/QString>
bool testGlobalScript(QString path);
bool testPlayerScript(QString path, int player, int difficulty);
#endif

View File

@ -11,7 +11,7 @@ int main(int argc, char **argv)
if (!fp)
{
fprintf(stderr, "maptest: Failed to open list file\n");
fprintf(stderr, "%s: Failed to open list file\n", argv[0]);
return -1;
}
PHYSFS_init(argv[0]);

View File

@ -242,7 +242,7 @@ int main(int argc, char **argv)
strcat(datapath, "/../data/");
while (!feof(fp))
{
char filename[PATH_MAX], *delim;
char filename[PATH_MAX];
fscanf(fp, "%s\n", &filename);
printf("Testing model: %s\n", filename);

19
tests/qslint.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QString>
#include "lint.h"
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
if (argc != 2)
{
fprintf(stderr, "Usage: %s <path to script file>\n", argv[0]);
exit(-1);
}
testGlobalScript(argv[1]);
return 0;
}

33
tests/qtscripttest.cpp Normal file
View File

@ -0,0 +1,33 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <QtCore/QCoreApplication>
#include "lint.h"
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
char datapath[PATH_MAX], fullpath[PATH_MAX], filename[PATH_MAX];
FILE *fp = fopen("jslist.txt", "r");
if (!fp)
{
fprintf(stderr, "%s: Failed to open list file\n", argv[0]);
return -1;
}
strcpy(datapath, getenv("srcdir"));
strcat(datapath, "/../data/");
while (!feof(fp))
{
fscanf(fp, "%s\n", &filename);
printf("Testing script: %s\n", filename);
strcpy(fullpath, datapath);
strcat(fullpath, filename);
testGlobalScript(fullpath);
}
fclose(fp);
return 0;
}