Index: binaries/data/mods/public/maps/random/rmgen/RandomMapLogger.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/RandomMapLogger.js +++ binaries/data/mods/public/maps/random/rmgen/RandomMapLogger.js @@ -1,14 +1,16 @@ function RandomMapLogger() { this.lastTime = undefined; - this.startTime = Engine.GetMicroseconds(); + this.startTime = Engine.GetMicroseconds ? Engine.GetMicroseconds() : 0; this.prefix = ""; // seems noisy - this.printDirectly( - this.prefix + - "Generating " + g_MapSettings.Name + - " of size " + g_MapSettings.Size + - " and " + getNumPlayers() + " players.\n"); + // Don't print in test cases + if (g_MapSettings.Name) + this.printDirectly( + this.prefix + + "Generating " + g_MapSettings.Name + + " of size " + g_MapSettings.Size + + " and " + (g_MapSettings.PlayerData.length - 1) + " players.\n"); } RandomMapLogger.prototype.printDirectly = function(string) Index: binaries/data/mods/public/maps/random/tests/test_TileClass.js =================================================================== --- /dev/null +++ binaries/data/mods/public/maps/random/tests/test_TileClass.js @@ -0,0 +1,92 @@ +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) + ]; + + absentPoints.forEach(point => { + 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); +} Index: source/graphics/MapGenerator.h =================================================================== --- source/graphics/MapGenerator.h +++ source/graphics/MapGenerator.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* 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 @@ -86,7 +86,7 @@ class CMapGeneratorWorker { public: - CMapGeneratorWorker(); + CMapGeneratorWorker(ScriptInterface* scriptInterface); ~CMapGeneratorWorker(); /** @@ -112,12 +112,17 @@ */ shared_ptr GetResults(); + /** + * Expose functions, globals and classes defined in this class to the map and test scripts. + */ + void RegisterScriptFunctions_Common(); + private: /** - * Expose functions defined in this class to the script. + * Expose functions to the mapscripts but not the tests. */ - void RegisterScriptFunctions(); + void RegisterScriptFunctions_MapGenerator(); /** * Load all scripts of the given library Index: source/graphics/MapGenerator.cpp =================================================================== --- source/graphics/MapGenerator.cpp +++ source/graphics/MapGenerator.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* 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 @@ -57,8 +57,14 @@ return true; } -CMapGeneratorWorker::CMapGeneratorWorker() +CMapGeneratorWorker::CMapGeneratorWorker(ScriptInterface* scriptInterface) : + m_ScriptInterface(scriptInterface) { + m_ScriptInterface->SetCallbackData(static_cast (this)); + + m_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG); + m_MapGenRNG.seed(0); + // If something happens before we initialize, that's a failure m_Progress = -1; } @@ -66,7 +72,8 @@ CMapGeneratorWorker::~CMapGeneratorWorker() { // 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) @@ -113,15 +120,13 @@ bool CMapGeneratorWorker::Run() { + // Keep this in sync with test_mapgen_scripts + JSContext* cx = m_ScriptInterface->GetContext(); JSAutoRequest rq(cx); - m_ScriptInterface->SetCallbackData(static_cast (this)); - - // Replace RNG with a seeded deterministic function - m_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG); - - RegisterScriptFunctions(); + RegisterScriptFunctions_Common(); + RegisterScriptFunctions_MapGenerator(); // Parse settings JS::RootedValue settingsVal(cx); @@ -165,7 +170,7 @@ return true; } -void CMapGeneratorWorker::RegisterScriptFunctions() +void CMapGeneratorWorker::RegisterScriptFunctions_Common() { // VFS JSI_VFS::RegisterScriptFunctions_Maps(*m_ScriptInterface); @@ -178,17 +183,6 @@ m_ScriptInterface->RegisterFunction("LoadHeightmapImage"); m_ScriptInterface->RegisterFunction("LoadMapTerrain"); - // Progression and profiling - m_ScriptInterface->RegisterFunction("SetProgress"); - m_ScriptInterface->RegisterFunction("GetMicroseconds"); - m_ScriptInterface->RegisterFunction("ExportMap"); - - // Template functions - m_ScriptInterface->RegisterFunction("GetTemplate"); - m_ScriptInterface->RegisterFunction("TemplateExists"); - m_ScriptInterface->RegisterFunction, std::string, bool, CMapGeneratorWorker::FindTemplates>("FindTemplates"); - m_ScriptInterface->RegisterFunction, std::string, bool, CMapGeneratorWorker::FindActorTemplates>("FindActorTemplates"); - // Engine constants // Length of one tile of the terrain grid in metres. @@ -199,6 +193,20 @@ m_ScriptInterface->SetGlobal("MAP_BORDER_WIDTH", static_cast(MAP_EDGE_TILES)); } +void CMapGeneratorWorker::RegisterScriptFunctions_MapGenerator() +{ + // Template functions + m_ScriptInterface->RegisterFunction("GetTemplate"); + m_ScriptInterface->RegisterFunction("TemplateExists"); + m_ScriptInterface->RegisterFunction, std::string, bool, CMapGeneratorWorker::FindTemplates>("FindTemplates"); + m_ScriptInterface->RegisterFunction, std::string, bool, CMapGeneratorWorker::FindActorTemplates>("FindActorTemplates"); + + // Progression and profiling + m_ScriptInterface->RegisterFunction("SetProgress"); + m_ScriptInterface->RegisterFunction("GetMicroseconds"); + m_ScriptInterface->RegisterFunction("ExportMap"); +} + int CMapGeneratorWorker::GetProgress() { std::lock_guard lock(m_WorkerMutex); @@ -410,7 +418,7 @@ ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// -CMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker()) +CMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker(nullptr)) { } Index: source/graphics/tests/test_MapGenerator.h =================================================================== --- /dev/null +++ source/graphics/tests/test_MapGenerator.h @@ -0,0 +1,56 @@ +/* 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 . + */ + +#include "simulation2/system/ComponentTest.h" + +#include "ps/Filesystem.h" +#include "graphics/MapGenerator.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); + + // Keep this in sync with CMapGeneratorWorker::Run + CMapGeneratorWorker worker(&scriptInterface); + worker.RegisterScriptFunctions_Common(); + scriptInterface.LoadGlobalScriptFile(path); + } + } +};