1
0
forked from 0ad/0ad

Support random map script tests, fixes #4827.

Differential Revision: https://code.wildfiregames.com/D2085
Comments By: wraitii
This was SVN commit r23455.
This commit is contained in:
elexis 2020-01-29 00:30:07 +00:00
parent 67a4c18c74
commit 0a6db43c83
5 changed files with 192 additions and 34 deletions

View File

@ -1,14 +1,16 @@
function RandomMapLogger() function RandomMapLogger()
{ {
this.lastTime = undefined; this.lastTime = undefined;
this.startTime = Engine.GetMicroseconds(); this.startTime = Engine.GetMicroseconds ? Engine.GetMicroseconds() : 0;
this.prefix = ""; // seems noisy this.prefix = ""; // seems noisy
this.printDirectly( // Don't print in test cases
this.prefix + if (g_MapSettings.Name)
"Generating " + g_MapSettings.Name + this.printDirectly(
" of size " + g_MapSettings.Size + this.prefix +
" and " + getNumPlayers() + " players.\n"); "Generating " + g_MapSettings.Name +
" of size " + g_MapSettings.Size +
" and " + (g_MapSettings.PlayerData.length - 1) + " players.\n");
} }
RandomMapLogger.prototype.printDirectly = function(string) RandomMapLogger.prototype.printDirectly = function(string)

View File

@ -0,0 +1,91 @@
Engine.LoadLibrary("rmgen");
var g_MapSettings = { "Size": 512 };
var g_Map = new RandomMap(0, "blackness");
// Test that that it checks by value, not by reference
{
let tileClass = new TileClass(2);
let reference1 = new Vector2D(1, 1);
let reference2 = new Vector2D(1, 1);
tileClass.add(reference1);
TS_ASSERT(tileClass.has(reference2));
}
// Test out-of-bounds
{
let tileClass = new TileClass(32);
let absentPoints = [
new Vector2D(0, 0),
new Vector2D(0, 1),
new Vector2D(1, 0),
new Vector2D(-1, -1),
new Vector2D(2048, 0),
new Vector2D(0, NaN),
new Vector2D(0, Infinity)
];
for (let point of absentPoints)
TS_ASSERT(!tileClass.has(point));
}
// Test multi-insertion
{
let tileClass = new TileClass(32);
let point = new Vector2D(1, 1);
tileClass.add(point);
tileClass.add(point);
TS_ASSERT_EQUALS(tileClass.countMembersInRadius(point, 0), 1);
// Still one point remaining
tileClass.remove(point);
tileClass.remove(point);
TS_ASSERT(!tileClass.has(point));
}
// Test multi-insertion removal
{
let tileClass = new TileClass(32);
let point = new Vector2D(2, 7);
tileClass.add(point);
tileClass.add(point);
tileClass.remove(point);
TS_ASSERT(tileClass.has(point));
tileClass.remove(point);
TS_ASSERT(!tileClass.has(point));
}
// Test multi-insertion removal
{
let tileClass = new TileClass(55);
let point = new Vector2D(5, 4);
for (let i = 0; i < 50; ++i)
tileClass.add(point);
tileClass.remove(point);
TS_ASSERT(tileClass.has(point));
tileClass.remove(point);
TS_ASSERT(tileClass.has(point));
}
// Test getters
{
let tileClass = new TileClass(88);
let point = new Vector2D(5, 1);
tileClass.add(point);
let point2 = new Vector2D(4, 9);
tileClass.add(point2);
TS_ASSERT_EQUALS(tileClass.countMembersInRadius(point, 1), 1);
TS_ASSERT_EQUALS(tileClass.countMembersInRadius(point, 100), 2);
TS_ASSERT_EQUALS(tileClass.countNonMembersInRadius(point, 1), 4);
TS_ASSERT_EQUALS(tileClass.countNonMembersInRadius(point, 2), 12);
TS_ASSERT_EQUALS(tileClass.countNonMembersInRadius(point, 3), 28);
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games. /* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -57,7 +57,8 @@ MapGeneratorInterruptCallback(JSContext* UNUSED(cx))
return true; return true;
} }
CMapGeneratorWorker::CMapGeneratorWorker() CMapGeneratorWorker::CMapGeneratorWorker(ScriptInterface* scriptInterface) :
m_ScriptInterface(scriptInterface)
{ {
// If something happens before we initialize, that's a failure // If something happens before we initialize, that's a failure
m_Progress = -1; m_Progress = -1;
@ -66,7 +67,8 @@ CMapGeneratorWorker::CMapGeneratorWorker()
CMapGeneratorWorker::~CMapGeneratorWorker() CMapGeneratorWorker::~CMapGeneratorWorker()
{ {
// Wait for thread to end // Wait for thread to end
m_WorkerThread.join(); if (m_WorkerThread.joinable())
m_WorkerThread.join();
} }
void CMapGeneratorWorker::Initialize(const VfsPath& scriptFile, const std::string& settings) void CMapGeneratorWorker::Initialize(const VfsPath& scriptFile, const std::string& settings)
@ -116,13 +118,6 @@ bool CMapGeneratorWorker::Run()
JSContext* cx = m_ScriptInterface->GetContext(); JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx); JSAutoRequest rq(cx);
m_ScriptInterface->SetCallbackData(static_cast<void*> (this));
// Replace RNG with a seeded deterministic function
m_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG);
RegisterScriptFunctions();
// Parse settings // Parse settings
JS::RootedValue settingsVal(cx); JS::RootedValue settingsVal(cx);
if (!m_ScriptInterface->ParseJSON(m_Settings, &settingsVal) && settingsVal.isUndefined()) if (!m_ScriptInterface->ParseJSON(m_Settings, &settingsVal) && settingsVal.isUndefined())
@ -144,7 +139,9 @@ bool CMapGeneratorWorker::Run()
!m_ScriptInterface->GetProperty(settingsVal, "Seed", seed)) !m_ScriptInterface->GetProperty(settingsVal, "Seed", seed))
LOGWARNING("CMapGeneratorWorker::Run: No seed value specified - using 0"); LOGWARNING("CMapGeneratorWorker::Run: No seed value specified - using 0");
m_MapGenRNG.seed(seed); InitScriptInterface(seed);
RegisterScriptFunctions_MapGenerator();
// Copy settings to global variable // Copy settings to global variable
JS::RootedValue global(cx, m_ScriptInterface->GetGlobalObject()); JS::RootedValue global(cx, m_ScriptInterface->GetGlobalObject());
@ -165,8 +162,13 @@ bool CMapGeneratorWorker::Run()
return true; return true;
} }
void CMapGeneratorWorker::RegisterScriptFunctions() void CMapGeneratorWorker::InitScriptInterface(const u32 seed)
{ {
m_ScriptInterface->SetCallbackData(static_cast<void*>(this));
m_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG);
m_MapGenRNG.seed(seed);
// VFS // VFS
JSI_VFS::RegisterScriptFunctions_Maps(*m_ScriptInterface); JSI_VFS::RegisterScriptFunctions_Maps(*m_ScriptInterface);
@ -178,17 +180,6 @@ void CMapGeneratorWorker::RegisterScriptFunctions()
m_ScriptInterface->RegisterFunction<JS::Value, VfsPath, CMapGeneratorWorker::LoadHeightmap>("LoadHeightmapImage"); m_ScriptInterface->RegisterFunction<JS::Value, VfsPath, CMapGeneratorWorker::LoadHeightmap>("LoadHeightmapImage");
m_ScriptInterface->RegisterFunction<JS::Value, VfsPath, CMapGeneratorWorker::LoadMapTerrain>("LoadMapTerrain"); m_ScriptInterface->RegisterFunction<JS::Value, VfsPath, CMapGeneratorWorker::LoadMapTerrain>("LoadMapTerrain");
// Progression and profiling
m_ScriptInterface->RegisterFunction<void, int, CMapGeneratorWorker::SetProgress>("SetProgress");
m_ScriptInterface->RegisterFunction<double, CMapGeneratorWorker::GetMicroseconds>("GetMicroseconds");
m_ScriptInterface->RegisterFunction<void, JS::HandleValue, CMapGeneratorWorker::ExportMap>("ExportMap");
// Template functions
m_ScriptInterface->RegisterFunction<CParamNode, std::string, CMapGeneratorWorker::GetTemplate>("GetTemplate");
m_ScriptInterface->RegisterFunction<bool, std::string, CMapGeneratorWorker::TemplateExists>("TemplateExists");
m_ScriptInterface->RegisterFunction<std::vector<std::string>, std::string, bool, CMapGeneratorWorker::FindTemplates>("FindTemplates");
m_ScriptInterface->RegisterFunction<std::vector<std::string>, std::string, bool, CMapGeneratorWorker::FindActorTemplates>("FindActorTemplates");
// Engine constants // Engine constants
// Length of one tile of the terrain grid in metres. // Length of one tile of the terrain grid in metres.
@ -199,6 +190,20 @@ void CMapGeneratorWorker::RegisterScriptFunctions()
m_ScriptInterface->SetGlobal("MAP_BORDER_WIDTH", static_cast<int>(MAP_EDGE_TILES)); m_ScriptInterface->SetGlobal("MAP_BORDER_WIDTH", static_cast<int>(MAP_EDGE_TILES));
} }
void CMapGeneratorWorker::RegisterScriptFunctions_MapGenerator()
{
// Template functions
m_ScriptInterface->RegisterFunction<CParamNode, std::string, CMapGeneratorWorker::GetTemplate>("GetTemplate");
m_ScriptInterface->RegisterFunction<bool, std::string, CMapGeneratorWorker::TemplateExists>("TemplateExists");
m_ScriptInterface->RegisterFunction<std::vector<std::string>, std::string, bool, CMapGeneratorWorker::FindTemplates>("FindTemplates");
m_ScriptInterface->RegisterFunction<std::vector<std::string>, std::string, bool, CMapGeneratorWorker::FindActorTemplates>("FindActorTemplates");
// Progression and profiling
m_ScriptInterface->RegisterFunction<void, int, CMapGeneratorWorker::SetProgress>("SetProgress");
m_ScriptInterface->RegisterFunction<double, CMapGeneratorWorker::GetMicroseconds>("GetMicroseconds");
m_ScriptInterface->RegisterFunction<void, JS::HandleValue, CMapGeneratorWorker::ExportMap>("ExportMap");
}
int CMapGeneratorWorker::GetProgress() int CMapGeneratorWorker::GetProgress()
{ {
std::lock_guard<std::mutex> lock(m_WorkerMutex); std::lock_guard<std::mutex> lock(m_WorkerMutex);
@ -410,7 +415,7 @@ JS::Value CMapGeneratorWorker::LoadMapTerrain(ScriptInterface::CxPrivate* pCxPri
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////
CMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker()) CMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker(nullptr))
{ {
} }

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games. /* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -86,7 +86,7 @@ private:
class CMapGeneratorWorker class CMapGeneratorWorker
{ {
public: public:
CMapGeneratorWorker(); CMapGeneratorWorker(ScriptInterface* scriptInterface);
~CMapGeneratorWorker(); ~CMapGeneratorWorker();
/** /**
@ -112,12 +112,18 @@ public:
*/ */
shared_ptr<ScriptInterface::StructuredClone> GetResults(); shared_ptr<ScriptInterface::StructuredClone> GetResults();
/**
* Set initial seed, callback data.
* Expose functions, globals and classes defined in this class relevant to the map and test scripts.
*/
void InitScriptInterface(const u32 seed);
private: private:
/** /**
* Expose functions defined in this class to the script. * Expose functions defined in this class that are relevant to mapscripts but not the tests.
*/ */
void RegisterScriptFunctions(); void RegisterScriptFunctions_MapGenerator();
/** /**
* Load all scripts of the given library * Load all scripts of the given library

View File

@ -0,0 +1,54 @@
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "graphics/MapGenerator.h"
#include "ps/Filesystem.h"
#include "simulation2/system/ComponentTest.h"
class TestMapGenerator : public CxxTest::TestSuite
{
public:
void setUp()
{
g_VFS = CreateVfs();
g_VFS->Mount(L"", DataDir() / "mods" / "mod", VFS_MOUNT_MUST_EXIST);
g_VFS->Mount(L"", DataDir() / "mods" / "public", VFS_MOUNT_MUST_EXIST, 1); // ignore directory-not-found errors
CXeromyces::Startup();
}
void tearDown()
{
CXeromyces::Terminate();
g_VFS.reset();
}
void test_mapgen_scripts()
{
VfsPaths paths;
TS_ASSERT_OK(vfs::GetPathnames(g_VFS, L"maps/random/tests/", L"test_*.js", paths));
for (const VfsPath& path : paths)
{
ScriptInterface scriptInterface("Engine", "MapGenerator", g_ScriptRuntime);
ScriptTestSetup(scriptInterface);
CMapGeneratorWorker worker(&scriptInterface);
worker.InitScriptInterface(0);
scriptInterface.LoadGlobalScriptFile(path);
}
}
};