Test: Add upstream tests
parent
24c8a8f887
commit
34668bff87
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file unix_main.cpp Main entry for Mac OSX. */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../openttd.h"
|
||||
#include "../../crashlog.h"
|
||||
#include "../../core/random_func.hpp"
|
||||
#include "../../string_func.h"
|
||||
#include "../../thread.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "macos.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
void CocoaSetupAutoreleasePool();
|
||||
void CocoaReleaseAutoreleasePool();
|
||||
|
||||
int CDECL main(int argc, char *argv[])
|
||||
{
|
||||
/* Make sure our arguments contain only valid UTF-8 characters. */
|
||||
for (int i = 0; i < argc; i++) StrMakeValidInPlace(argv[i]);
|
||||
|
||||
CocoaSetupAutoreleasePool();
|
||||
/* This is passed if we are launched by double-clicking */
|
||||
if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) {
|
||||
argv[1] = nullptr;
|
||||
argc = 1;
|
||||
}
|
||||
|
||||
PerThreadSetupInit();
|
||||
CrashLog::InitialiseCrashLog();
|
||||
|
||||
SetRandomSeed(time(nullptr));
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
int ret = openttd_main(argc, argv);
|
||||
|
||||
CocoaReleaseAutoreleasePool();
|
||||
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file unix_main.cpp Main entry for Unix. */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../openttd.h"
|
||||
#include "../../crashlog.h"
|
||||
#include "../../core/random_func.hpp"
|
||||
#include "../../string_func.h"
|
||||
#include "../../thread.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
int CDECL main(int argc, char *argv[])
|
||||
{
|
||||
/* Make sure our arguments contain only valid UTF-8 characters. */
|
||||
for (int i = 0; i < argc; i++) StrMakeValidInPlace(argv[i]);
|
||||
|
||||
PerThreadSetupInit();
|
||||
CrashLog::InitialiseCrashLog();
|
||||
|
||||
SetRandomSeed(time(nullptr));
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
return openttd_main(argc, argv);
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file win32_main.cpp Implementation main for Windows. */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include "../../openttd.h"
|
||||
#include "../../core/random_func.hpp"
|
||||
#include "../../string_func.h"
|
||||
#include "../../crashlog.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../thread.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
static int ParseCommandLine(char *line, char **argv, int max_argc)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
do {
|
||||
/* skip whitespace */
|
||||
while (*line == ' ' || *line == '\t') line++;
|
||||
|
||||
/* end? */
|
||||
if (*line == '\0') break;
|
||||
|
||||
/* special handling when quoted */
|
||||
if (*line == '"') {
|
||||
argv[n++] = ++line;
|
||||
while (*line != '"') {
|
||||
if (*line == '\0') return n;
|
||||
line++;
|
||||
}
|
||||
} else {
|
||||
argv[n++] = line;
|
||||
while (*line != ' ' && *line != '\t') {
|
||||
if (*line == '\0') return n;
|
||||
line++;
|
||||
}
|
||||
}
|
||||
*line++ = '\0';
|
||||
} while (n != max_argc);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void CreateConsole();
|
||||
|
||||
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||
{
|
||||
int argc;
|
||||
char *argv[64]; // max 64 command line arguments
|
||||
|
||||
/* Set system timer resolution to 1ms. */
|
||||
timeBeginPeriod(1);
|
||||
|
||||
PerThreadSetupInit();
|
||||
CrashLog::InitialiseCrashLog();
|
||||
|
||||
/* Convert the command line to UTF-8. */
|
||||
std::string cmdline = FS2OTTD(GetCommandLine());
|
||||
|
||||
/* Set the console codepage to UTF-8. */
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
CreateConsole();
|
||||
#endif
|
||||
|
||||
_set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
|
||||
|
||||
/* setup random seed to something quite random */
|
||||
SetRandomSeed(GetTickCount());
|
||||
|
||||
argc = ParseCommandLine(cmdline.data(), argv, lengthof(argv));
|
||||
|
||||
/* Make sure our arguments contain only valid UTF-8 characters. */
|
||||
for (int i = 0; i < argc; i++) StrMakeValidInPlace(argv[i]);
|
||||
|
||||
openttd_main(argc, argv);
|
||||
|
||||
/* Restore system timer resolution. */
|
||||
timeEndPeriod(1);
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file mock_environment.h Singleton instance to create a mock FontCache/SpriteCache environment. */
|
||||
|
||||
#ifndef MOCK_ENVIRONMENT_H
|
||||
#define MOCK_ENVIRONMENT_H
|
||||
|
||||
#include "mock_fontcache.h"
|
||||
#include "mock_spritecache.h"
|
||||
|
||||
/** Singleton class to set up the mock environemnt once. */
|
||||
class MockEnvironment {
|
||||
public:
|
||||
static MockEnvironment &Instance()
|
||||
{
|
||||
static MockEnvironment instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
MockEnvironment(MockEnvironment const &) = delete;
|
||||
void operator=(MockEnvironment const &) = delete;
|
||||
|
||||
private:
|
||||
MockEnvironment()
|
||||
{
|
||||
/* Mock SpriteCache initialization is needed for some widget generators. */
|
||||
MockGfxLoadSprites();
|
||||
|
||||
/* Mock FontCache initialization is needed for some NWidgetParts. */
|
||||
MockFontCache::InitializeFontCaches();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* MOCK_ENVIRONMENT_H */
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file mock_fontcache.h Mock font cache implementation definition. */
|
||||
|
||||
#ifndef MOCK_FONTCACHE_H
|
||||
#define MOCK_FONTCACHE_H
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../fontcache.h"
|
||||
#include "../string_func.h"
|
||||
|
||||
/** Font cache for mocking basic use of fonts. */
|
||||
class MockFontCache : public FontCache {
|
||||
public:
|
||||
MockFontCache(FontSize fs) : FontCache(fs)
|
||||
{
|
||||
this->height = FontCache::GetDefaultFontHeight(this->fs);
|
||||
}
|
||||
|
||||
void SetUnicodeGlyph(char32_t, SpriteID) override {}
|
||||
void InitializeUnicodeGlyphMap() override {}
|
||||
void ClearFontCache() override {}
|
||||
const Sprite *GetGlyph(GlyphID) override { return nullptr; }
|
||||
uint GetGlyphWidth(GlyphID) override { return this->height / 2; }
|
||||
bool GetDrawGlyphShadow() override { return false; }
|
||||
GlyphID MapCharToGlyph(char32_t key) override { return key; }
|
||||
const void *GetFontTable(uint32_t, size_t &length) override { length = 0; return nullptr; }
|
||||
std::string GetFontName() override { return "mock"; }
|
||||
bool IsBuiltInFont() override { return true; }
|
||||
|
||||
static void InitializeFontCaches()
|
||||
{
|
||||
for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) {
|
||||
if (FontCache::caches[fs] == nullptr) new MockFontCache(fs); /* FontCache inserts itself into to the cache. */
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* MOCK_FONTCACHE_H */
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file mock_spritecache.cpp Mock sprite cache implementation. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../blitter/factory.hpp"
|
||||
#include "../core/math_func.hpp"
|
||||
#include "../spritecache.h"
|
||||
#include "../spritecache_internal.h"
|
||||
#include "../table/sprites.h"
|
||||
|
||||
static bool MockLoadNextSprite(int load_index)
|
||||
{
|
||||
SpriteDataBuffer buffer;
|
||||
buffer.Allocate((uint32)sizeof(Sprite));
|
||||
memset(buffer.GetPtr(), 0, buffer.GetSize());
|
||||
|
||||
bool is_mapgen = IsMapgenSpriteID(load_index);
|
||||
|
||||
SpriteCache *sc = AllocateSpriteCache(load_index);
|
||||
sc->file = nullptr;
|
||||
sc->file_pos = 0;
|
||||
sc->Assign(std::move(buffer));
|
||||
sc->id = 0;
|
||||
sc->type = is_mapgen ? SpriteType::MapGen : SpriteType::Normal;
|
||||
sc->flags = 0;
|
||||
|
||||
/* Fill with empty sprites up until the default sprite count. */
|
||||
return (uint)load_index < SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT;
|
||||
}
|
||||
|
||||
void MockGfxLoadSprites()
|
||||
{
|
||||
/* Force blitter 'null'. This is necessary for GfxInitSpriteMem() to function. */
|
||||
BlitterFactory::SelectBlitter("null");
|
||||
|
||||
GfxInitSpriteMem();
|
||||
|
||||
int load_index = 0;
|
||||
while (MockLoadNextSprite(load_index)) {
|
||||
load_index++;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file mock_spritecache.h Mock sprite cache definition. */
|
||||
|
||||
#ifndef MOCK_SPRITECACHE_H
|
||||
#define MOCK_SPRITECACHE_H
|
||||
|
||||
void MockGfxLoadSprites();
|
||||
|
||||
#endif /* MOCK_SPRITECACHE_H */
|
@ -0,0 +1,527 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file string_func.cpp Test functionality from string_func. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../3rdparty/catch2/catch.hpp"
|
||||
|
||||
#include "../string_func.h"
|
||||
#include <array>
|
||||
|
||||
/**** String compare/equals *****/
|
||||
|
||||
TEST_CASE("StrCompareIgnoreCase - std::string")
|
||||
{
|
||||
/* Same string, with different cases. */
|
||||
CHECK(StrCompareIgnoreCase(std::string{""}, std::string{""}) == 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"a"}) == 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"A"}) == 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"A"}, std::string{"a"}) == 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"A"}, std::string{"A"}) == 0);
|
||||
|
||||
/* Not the same string. */
|
||||
CHECK(StrCompareIgnoreCase(std::string{""}, std::string{"b"}) < 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{""}) > 0);
|
||||
|
||||
CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"b"}) < 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"b"}, std::string{"a"}) > 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"B"}) < 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"b"}, std::string{"A"}) > 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"A"}, std::string{"b"}) < 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"B"}, std::string{"a"}) > 0);
|
||||
|
||||
CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"aa"}) < 0);
|
||||
CHECK(StrCompareIgnoreCase(std::string{"aa"}, std::string{"a"}) > 0);
|
||||
}
|
||||
|
||||
TEST_CASE("StrCompareIgnoreCase - char pointer")
|
||||
{
|
||||
/* Same string, with different cases. */
|
||||
CHECK(StrCompareIgnoreCase("", "") == 0);
|
||||
CHECK(StrCompareIgnoreCase("a", "a") == 0);
|
||||
CHECK(StrCompareIgnoreCase("a", "A") == 0);
|
||||
CHECK(StrCompareIgnoreCase("A", "a") == 0);
|
||||
CHECK(StrCompareIgnoreCase("A", "A") == 0);
|
||||
|
||||
/* Not the same string. */
|
||||
CHECK(StrCompareIgnoreCase("", "b") < 0);
|
||||
CHECK(StrCompareIgnoreCase("a", "") > 0);
|
||||
|
||||
CHECK(StrCompareIgnoreCase("a", "b") < 0);
|
||||
CHECK(StrCompareIgnoreCase("b", "a") > 0);
|
||||
CHECK(StrCompareIgnoreCase("a", "B") < 0);
|
||||
CHECK(StrCompareIgnoreCase("b", "A") > 0);
|
||||
CHECK(StrCompareIgnoreCase("A", "b") < 0);
|
||||
CHECK(StrCompareIgnoreCase("B", "a") > 0);
|
||||
|
||||
CHECK(StrCompareIgnoreCase("a", "aa") < 0);
|
||||
CHECK(StrCompareIgnoreCase("aa", "a") > 0);
|
||||
}
|
||||
|
||||
TEST_CASE("StrCompareIgnoreCase - std::string_view")
|
||||
{
|
||||
/*
|
||||
* With std::string_view the only way to access the data is via .data(),
|
||||
* which does not guarantee the termination that would be required by
|
||||
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
|
||||
* or strcasecmp would fail if it does not account for the length of the
|
||||
* view. Thus, contrary to the string/char* tests, this uses the same base
|
||||
* string but gets different sections to trigger these corner cases.
|
||||
*/
|
||||
std::string_view base{"aaAbB"};
|
||||
|
||||
/* Same string, with different cases. */
|
||||
CHECK(StrCompareIgnoreCase(base.substr(0, 0), base.substr(1, 0)) == 0); // Different positions
|
||||
CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(1, 1)) == 0); // Different positions
|
||||
CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(2, 1)) == 0);
|
||||
CHECK(StrCompareIgnoreCase(base.substr(2, 1), base.substr(1, 1)) == 0);
|
||||
CHECK(StrCompareIgnoreCase(base.substr(2, 1), base.substr(2, 1)) == 0);
|
||||
|
||||
/* Not the same string. */
|
||||
CHECK(StrCompareIgnoreCase(base.substr(3, 0), base.substr(3, 1)) < 0); // Same position, different lengths
|
||||
CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(0, 0)) > 0); // Same position, different lengths
|
||||
|
||||
CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(3, 1)) < 0);
|
||||
CHECK(StrCompareIgnoreCase(base.substr(3, 1), base.substr(0, 1)) > 0);
|
||||
CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(4, 1)) < 0);
|
||||
CHECK(StrCompareIgnoreCase(base.substr(3, 1), base.substr(2, 1)) > 0);
|
||||
CHECK(StrCompareIgnoreCase(base.substr(2, 1), base.substr(3, 1)) < 0);
|
||||
CHECK(StrCompareIgnoreCase(base.substr(4, 1), base.substr(0, 1)) > 0);
|
||||
|
||||
CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(0, 2)) < 0); // Same position, different lengths
|
||||
CHECK(StrCompareIgnoreCase(base.substr(0, 2), base.substr(0, 1)) > 0); // Same position, different lengths
|
||||
}
|
||||
|
||||
TEST_CASE("StrEqualsIgnoreCase - std::string")
|
||||
{
|
||||
/* Same string, with different cases. */
|
||||
CHECK(StrEqualsIgnoreCase(std::string{""}, std::string{""}));
|
||||
CHECK(StrEqualsIgnoreCase(std::string{"a"}, std::string{"a"}));
|
||||
CHECK(StrEqualsIgnoreCase(std::string{"a"}, std::string{"A"}));
|
||||
CHECK(StrEqualsIgnoreCase(std::string{"A"}, std::string{"a"}));
|
||||
CHECK(StrEqualsIgnoreCase(std::string{"A"}, std::string{"A"}));
|
||||
|
||||
/* Not the same string. */
|
||||
CHECK(!StrEqualsIgnoreCase(std::string{""}, std::string{"b"}));
|
||||
CHECK(!StrEqualsIgnoreCase(std::string{"a"}, std::string{""}));
|
||||
CHECK(!StrEqualsIgnoreCase(std::string{"a"}, std::string{"b"}));
|
||||
CHECK(!StrEqualsIgnoreCase(std::string{"b"}, std::string{"a"}));
|
||||
CHECK(!StrEqualsIgnoreCase(std::string{"a"}, std::string{"aa"}));
|
||||
CHECK(!StrEqualsIgnoreCase(std::string{"aa"}, std::string{"a"}));
|
||||
}
|
||||
|
||||
TEST_CASE("StrEqualsIgnoreCase - char pointer")
|
||||
{
|
||||
/* Same string, with different cases. */
|
||||
CHECK(StrEqualsIgnoreCase("", ""));
|
||||
CHECK(StrEqualsIgnoreCase("a", "a"));
|
||||
CHECK(StrEqualsIgnoreCase("a", "A"));
|
||||
CHECK(StrEqualsIgnoreCase("A", "a"));
|
||||
CHECK(StrEqualsIgnoreCase("A", "A"));
|
||||
|
||||
/* Not the same string. */
|
||||
CHECK(!StrEqualsIgnoreCase("", "b"));
|
||||
CHECK(!StrEqualsIgnoreCase("a", ""));
|
||||
CHECK(!StrEqualsIgnoreCase("a", "b"));
|
||||
CHECK(!StrEqualsIgnoreCase("b", "a"));
|
||||
CHECK(!StrEqualsIgnoreCase("a", "aa"));
|
||||
CHECK(!StrEqualsIgnoreCase("aa", "a"));
|
||||
}
|
||||
|
||||
TEST_CASE("StrEqualsIgnoreCase - std::string_view")
|
||||
{
|
||||
/*
|
||||
* With std::string_view the only way to access the data is via .data(),
|
||||
* which does not guarantee the termination that would be required by
|
||||
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
|
||||
* or strcasecmp would fail if it does not account for the length of the
|
||||
* view. Thus, contrary to the string/char* tests, this uses the same base
|
||||
* string but gets different sections to trigger these corner cases.
|
||||
*/
|
||||
std::string_view base{"aaAb"};
|
||||
|
||||
/* Same string, with different cases. */
|
||||
CHECK(StrEqualsIgnoreCase(base.substr(0, 0), base.substr(1, 0))); // Different positions
|
||||
CHECK(StrEqualsIgnoreCase(base.substr(0, 1), base.substr(1, 1))); // Different positions
|
||||
CHECK(StrEqualsIgnoreCase(base.substr(0, 1), base.substr(2, 1)));
|
||||
CHECK(StrEqualsIgnoreCase(base.substr(2, 1), base.substr(1, 1)));
|
||||
CHECK(StrEqualsIgnoreCase(base.substr(2, 1), base.substr(2, 1)));
|
||||
|
||||
/* Not the same string. */
|
||||
CHECK(!StrEqualsIgnoreCase(base.substr(3, 0), base.substr(3, 1))); // Same position, different lengths
|
||||
CHECK(!StrEqualsIgnoreCase(base.substr(0, 1), base.substr(0, 0)));
|
||||
CHECK(!StrEqualsIgnoreCase(base.substr(0, 1), base.substr(3, 1)));
|
||||
CHECK(!StrEqualsIgnoreCase(base.substr(3, 1), base.substr(0, 1)));
|
||||
CHECK(!StrEqualsIgnoreCase(base.substr(0, 1), base.substr(0, 2))); // Same position, different lengths
|
||||
CHECK(!StrEqualsIgnoreCase(base.substr(0, 2), base.substr(0, 1))); // Same position, different lengths
|
||||
}
|
||||
|
||||
/**** String starts with *****/
|
||||
|
||||
TEST_CASE("StrStartsWith - std::string")
|
||||
{
|
||||
/* Everything starts with an empty prefix. */
|
||||
CHECK(StrStartsWith(std::string{""}, std::string{""}));
|
||||
CHECK(StrStartsWith(std::string{"a"}, std::string{""}));
|
||||
|
||||
/* Equal strings. */
|
||||
CHECK(StrStartsWith(std::string{"a"}, std::string{"a"}));
|
||||
CHECK(StrStartsWith(std::string{"A"}, std::string{"A"}));
|
||||
|
||||
/* Starts with same. */
|
||||
CHECK(StrStartsWith(std::string{"ab"}, std::string{"a"}));
|
||||
CHECK(StrStartsWith(std::string{"Ab"}, std::string{"A"}));
|
||||
|
||||
/* Different cases. */
|
||||
CHECK(!StrStartsWith(std::string{"a"}, std::string{"A"}));
|
||||
CHECK(!StrStartsWith(std::string{"A"}, std::string{"a"}));
|
||||
CHECK(!StrStartsWith(std::string{"ab"}, std::string{"A"}));
|
||||
CHECK(!StrStartsWith(std::string{"Ab"}, std::string{"a"}));
|
||||
|
||||
/* Does not start the same. */
|
||||
CHECK(!StrStartsWith(std::string{""}, std::string{"b"}));
|
||||
CHECK(!StrStartsWith(std::string{"a"}, std::string{"b"}));
|
||||
CHECK(!StrStartsWith(std::string{"b"}, std::string{"a"}));
|
||||
CHECK(!StrStartsWith(std::string{"a"}, std::string{"aa"}));
|
||||
}
|
||||
|
||||
TEST_CASE("StrStartsWith - char pointer")
|
||||
{
|
||||
CHECK(StrStartsWith("", ""));
|
||||
CHECK(StrStartsWith("a", ""));
|
||||
|
||||
/* Equal strings. */
|
||||
CHECK(StrStartsWith("a", "a"));
|
||||
CHECK(StrStartsWith("A", "A"));
|
||||
|
||||
/* Starts with same. */
|
||||
CHECK(StrStartsWith("ab", "a"));
|
||||
CHECK(StrStartsWith("Ab", "A"));
|
||||
|
||||
/* Different cases. */
|
||||
CHECK(!StrStartsWith("a", "A"));
|
||||
CHECK(!StrStartsWith("A", "a"));
|
||||
CHECK(!StrStartsWith("ab", "A"));
|
||||
CHECK(!StrStartsWith("Ab", "a"));
|
||||
|
||||
/* Does not start the same. */
|
||||
CHECK(!StrStartsWith("", "b"));
|
||||
CHECK(!StrStartsWith("a", "b"));
|
||||
CHECK(!StrStartsWith("b", "a"));
|
||||
CHECK(!StrStartsWith("a", "aa"));
|
||||
}
|
||||
|
||||
TEST_CASE("StrStartsWith - std::string_view")
|
||||
{
|
||||
/*
|
||||
* With std::string_view the only way to access the data is via .data(),
|
||||
* which does not guarantee the termination that would be required by
|
||||
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
|
||||
* or strcasecmp would fail if it does not account for the length of the
|
||||
* view. Thus, contrary to the string/char* tests, this uses the same base
|
||||
* string but gets different sections to trigger these corner cases.
|
||||
*/
|
||||
std::string_view base{"aabAb"};
|
||||
|
||||
/* Everything starts with an empty prefix. */
|
||||
CHECK(StrStartsWith(base.substr(0, 0), base.substr(1, 0))); // Different positions
|
||||
CHECK(StrStartsWith(base.substr(0, 1), base.substr(0, 0)));
|
||||
|
||||
/* Equals string. */
|
||||
CHECK(StrStartsWith(base.substr(0, 1), base.substr(1, 1))); // Different positions
|
||||
CHECK(StrStartsWith(base.substr(3, 1), base.substr(3, 1)));
|
||||
|
||||
/* Starts with same. */
|
||||
CHECK(StrStartsWith(base.substr(1, 2), base.substr(0, 1)));
|
||||
CHECK(StrStartsWith(base.substr(3, 2), base.substr(3, 1)));
|
||||
|
||||
/* Different cases. */
|
||||
CHECK(!StrStartsWith(base.substr(0, 1), base.substr(3, 1)));
|
||||
CHECK(!StrStartsWith(base.substr(3, 1), base.substr(0, 1)));
|
||||
CHECK(!StrStartsWith(base.substr(1, 2), base.substr(3, 1)));
|
||||
CHECK(!StrStartsWith(base.substr(3, 2), base.substr(0, 1)));
|
||||
|
||||
/* Does not start the same. */
|
||||
CHECK(!StrStartsWith(base.substr(2, 0), base.substr(2, 1)));
|
||||
CHECK(!StrStartsWith(base.substr(0, 1), base.substr(2, 1)));
|
||||
CHECK(!StrStartsWith(base.substr(2, 1), base.substr(0, 1)));
|
||||
CHECK(!StrStartsWith(base.substr(0, 1), base.substr(0, 2)));
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("StrStartsWithIgnoreCase - std::string")
|
||||
{
|
||||
/* Everything starts with an empty prefix. */
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{""}, std::string{""}));
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{"a"}, std::string{""}));
|
||||
|
||||
/* Equals string, ignoring case. */
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{"a"}, std::string{"a"}));
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{"a"}, std::string{"A"}));
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{"A"}, std::string{"a"}));
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{"A"}, std::string{"A"}));
|
||||
|
||||
/* Starts with same, ignoring case. */
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{"ab"}, std::string{"a"}));
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{"ab"}, std::string{"A"}));
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{"Ab"}, std::string{"a"}));
|
||||
CHECK(StrStartsWithIgnoreCase(std::string{"Ab"}, std::string{"A"}));
|
||||
|
||||
/* Does not start the same. */
|
||||
CHECK(!StrStartsWithIgnoreCase(std::string{""}, std::string{"b"}));
|
||||
CHECK(!StrStartsWithIgnoreCase(std::string{"a"}, std::string{"b"}));
|
||||
CHECK(!StrStartsWithIgnoreCase(std::string{"b"}, std::string{"a"}));
|
||||
CHECK(!StrStartsWithIgnoreCase(std::string{"a"}, std::string{"aa"}));
|
||||
}
|
||||
|
||||
TEST_CASE("StrStartsWithIgnoreCase - char pointer")
|
||||
{
|
||||
/* Everything starts with an empty prefix. */
|
||||
CHECK(StrStartsWithIgnoreCase("", ""));
|
||||
CHECK(StrStartsWithIgnoreCase("a", ""));
|
||||
|
||||
/* Equals string, ignoring case. */
|
||||
CHECK(StrStartsWithIgnoreCase("a", "a"));
|
||||
CHECK(StrStartsWithIgnoreCase("a", "A"));
|
||||
CHECK(StrStartsWithIgnoreCase("A", "a"));
|
||||
CHECK(StrStartsWithIgnoreCase("A", "A"));
|
||||
|
||||
/* Starts with same, ignoring case. */
|
||||
CHECK(StrStartsWithIgnoreCase("ab", "a"));
|
||||
CHECK(StrStartsWithIgnoreCase("ab", "A"));
|
||||
CHECK(StrStartsWithIgnoreCase("Ab", "a"));
|
||||
CHECK(StrStartsWithIgnoreCase("Ab", "A"));
|
||||
|
||||
/* Does not start the same. */
|
||||
CHECK(!StrStartsWithIgnoreCase("", "b"));
|
||||
CHECK(!StrStartsWithIgnoreCase("a", "b"));
|
||||
CHECK(!StrStartsWithIgnoreCase("b", "a"));
|
||||
CHECK(!StrStartsWithIgnoreCase("a", "aa"));
|
||||
}
|
||||
|
||||
TEST_CASE("StrStartsWithIgnoreCase - std::string_view")
|
||||
{
|
||||
/*
|
||||
* With std::string_view the only way to access the data is via .data(),
|
||||
* which does not guarantee the termination that would be required by
|
||||
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
|
||||
* or strcasecmp would fail if it does not account for the length of the
|
||||
* view. Thus, contrary to the string/char* tests, this uses the same base
|
||||
* string but gets different sections to trigger these corner cases.
|
||||
*/
|
||||
std::string_view base{"aabAb"};
|
||||
|
||||
/* Everything starts with an empty prefix. */
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(0, 0), base.substr(1, 0))); // Different positions
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(0, 0)));
|
||||
|
||||
/* Equals string, ignoring case. */
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(1, 1))); // Different positions
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(3, 1)));
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(3, 1), base.substr(0, 1)));
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(3, 1), base.substr(3, 1)));
|
||||
|
||||
/* Starts with same, ignoring case. */
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(1, 2), base.substr(0, 1)));
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(1, 2), base.substr(3, 1)));
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(3, 2), base.substr(0, 1)));
|
||||
CHECK(StrStartsWithIgnoreCase(base.substr(3, 2), base.substr(3, 1)));
|
||||
|
||||
/* Does not start the same. */
|
||||
CHECK(!StrStartsWithIgnoreCase(base.substr(2, 0), base.substr(2, 1)));
|
||||
CHECK(!StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(2, 1)));
|
||||
CHECK(!StrStartsWithIgnoreCase(base.substr(2, 1), base.substr(0, 1)));
|
||||
CHECK(!StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(0, 2)));
|
||||
}
|
||||
|
||||
/**** String ends with *****/
|
||||
|
||||
TEST_CASE("StrEndsWith - std::string")
|
||||
{
|
||||
/* Everything ends with an empty prefix. */
|
||||
CHECK(StrEndsWith(std::string{""}, std::string{""}));
|
||||
CHECK(StrEndsWith(std::string{"a"}, std::string{""}));
|
||||
|
||||
/* Equal strings. */
|
||||
CHECK(StrEndsWith(std::string{"a"}, std::string{"a"}));
|
||||
CHECK(StrEndsWith(std::string{"A"}, std::string{"A"}));
|
||||
|
||||
/* Ends with same. */
|
||||
CHECK(StrEndsWith(std::string{"ba"}, std::string{"a"}));
|
||||
CHECK(StrEndsWith(std::string{"bA"}, std::string{"A"}));
|
||||
|
||||
/* Different cases. */
|
||||
CHECK(!StrEndsWith(std::string{"a"}, std::string{"A"}));
|
||||
CHECK(!StrEndsWith(std::string{"A"}, std::string{"a"}));
|
||||
CHECK(!StrEndsWith(std::string{"ba"}, std::string{"A"}));
|
||||
CHECK(!StrEndsWith(std::string{"bA"}, std::string{"a"}));
|
||||
|
||||
/* Does not end the same. */
|
||||
CHECK(!StrEndsWith(std::string{""}, std::string{"b"}));
|
||||
CHECK(!StrEndsWith(std::string{"a"}, std::string{"b"}));
|
||||
CHECK(!StrEndsWith(std::string{"b"}, std::string{"a"}));
|
||||
CHECK(!StrEndsWith(std::string{"a"}, std::string{"aa"}));
|
||||
}
|
||||
|
||||
TEST_CASE("StrEndsWith - char pointer")
|
||||
{
|
||||
CHECK(StrEndsWith("", ""));
|
||||
CHECK(StrEndsWith("a", ""));
|
||||
|
||||
/* Equal strings. */
|
||||
CHECK(StrEndsWith("a", "a"));
|
||||
CHECK(StrEndsWith("A", "A"));
|
||||
|
||||
/* Ends with same. */
|
||||
CHECK(StrEndsWith("ba", "a"));
|
||||
CHECK(StrEndsWith("bA", "A"));
|
||||
|
||||
/* Different cases. */
|
||||
CHECK(!StrEndsWith("a", "A"));
|
||||
CHECK(!StrEndsWith("A", "a"));
|
||||
CHECK(!StrEndsWith("ba", "A"));
|
||||
CHECK(!StrEndsWith("bA", "a"));
|
||||
|
||||
/* Does not end the same. */
|
||||
CHECK(!StrEndsWith("", "b"));
|
||||
CHECK(!StrEndsWith("a", "b"));
|
||||
CHECK(!StrEndsWith("b", "a"));
|
||||
CHECK(!StrEndsWith("a", "aa"));
|
||||
}
|
||||
|
||||
TEST_CASE("StrEndsWith - std::string_view")
|
||||
{
|
||||
/*
|
||||
* With std::string_view the only way to access the data is via .data(),
|
||||
* which does not guarantee the termination that would be required by
|
||||
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
|
||||
* or strcasecmp would fail if it does not account for the length of the
|
||||
* view. Thus, contrary to the string/char* tests, this uses the same base
|
||||
* string but gets different sections to trigger these corner cases.
|
||||
*/
|
||||
std::string_view base{"aabAba"};
|
||||
|
||||
/* Everything ends with an empty prefix. */
|
||||
CHECK(StrEndsWith(base.substr(0, 0), base.substr(1, 0))); // Different positions
|
||||
CHECK(StrEndsWith(base.substr(0, 1), base.substr(0, 0)));
|
||||
|
||||
/* Equals string. */
|
||||
CHECK(StrEndsWith(base.substr(0, 1), base.substr(1, 1))); // Different positions
|
||||
CHECK(StrEndsWith(base.substr(3, 1), base.substr(3, 1)));
|
||||
|
||||
/* Ends with same. */
|
||||
CHECK(StrEndsWith(base.substr(4, 2), base.substr(0, 1)));
|
||||
CHECK(StrEndsWith(base.substr(2, 2), base.substr(3, 1)));
|
||||
|
||||
/* Different cases. */
|
||||
CHECK(!StrEndsWith(base.substr(0, 1), base.substr(3, 1)));
|
||||
CHECK(!StrEndsWith(base.substr(3, 1), base.substr(0, 1)));
|
||||
CHECK(!StrEndsWith(base.substr(4, 2), base.substr(3, 1)));
|
||||
CHECK(!StrEndsWith(base.substr(2, 2), base.substr(0, 1)));
|
||||
|
||||
/* Does not end the same. */
|
||||
CHECK(!StrEndsWith(base.substr(2, 0), base.substr(2, 1)));
|
||||
CHECK(!StrEndsWith(base.substr(0, 1), base.substr(2, 1)));
|
||||
CHECK(!StrEndsWith(base.substr(2, 1), base.substr(0, 1)));
|
||||
CHECK(!StrEndsWith(base.substr(0, 1), base.substr(0, 2)));
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("StrEndsWithIgnoreCase - std::string")
|
||||
{
|
||||
/* Everything ends with an empty prefix. */
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{""}, std::string{""}));
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{"a"}, std::string{""}));
|
||||
|
||||
/* Equals string, ignoring case. */
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{"a"}, std::string{"a"}));
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{"a"}, std::string{"A"}));
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{"A"}, std::string{"a"}));
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{"A"}, std::string{"A"}));
|
||||
|
||||
/* Ends with same, ignoring case. */
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{"ba"}, std::string{"a"}));
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{"ba"}, std::string{"A"}));
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{"bA"}, std::string{"a"}));
|
||||
CHECK(StrEndsWithIgnoreCase(std::string{"bA"}, std::string{"A"}));
|
||||
|
||||
/* Does not end the same. */
|
||||
CHECK(!StrEndsWithIgnoreCase(std::string{""}, std::string{"b"}));
|
||||
CHECK(!StrEndsWithIgnoreCase(std::string{"a"}, std::string{"b"}));
|
||||
CHECK(!StrEndsWithIgnoreCase(std::string{"b"}, std::string{"a"}));
|
||||
CHECK(!StrEndsWithIgnoreCase(std::string{"a"}, std::string{"aa"}));
|
||||
}
|
||||
|
||||
TEST_CASE("StrEndsWithIgnoreCase - char pointer")
|
||||
{
|
||||
/* Everything ends with an empty prefix. */
|
||||
CHECK(StrEndsWithIgnoreCase("", ""));
|
||||
CHECK(StrEndsWithIgnoreCase("a", ""));
|
||||
|
||||
/* Equals string, ignoring case. */
|
||||
CHECK(StrEndsWithIgnoreCase("a", "a"));
|
||||
CHECK(StrEndsWithIgnoreCase("a", "A"));
|
||||
CHECK(StrEndsWithIgnoreCase("A", "a"));
|
||||
CHECK(StrEndsWithIgnoreCase("A", "A"));
|
||||
|
||||
/* Ends with same, ignoring case. */
|
||||
CHECK(StrEndsWithIgnoreCase("ba", "a"));
|
||||
CHECK(StrEndsWithIgnoreCase("ba", "A"));
|
||||
CHECK(StrEndsWithIgnoreCase("bA", "a"));
|
||||
CHECK(StrEndsWithIgnoreCase("bA", "A"));
|
||||
|
||||
/* Does not end the same. */
|
||||
CHECK(!StrEndsWithIgnoreCase("", "b"));
|
||||
CHECK(!StrEndsWithIgnoreCase("a", "b"));
|
||||
CHECK(!StrEndsWithIgnoreCase("b", "a"));
|
||||
CHECK(!StrEndsWithIgnoreCase("a", "aa"));
|
||||
}
|
||||
|
||||
TEST_CASE("StrEndsWithIgnoreCase - std::string_view")
|
||||
{
|
||||
/*
|
||||
* With std::string_view the only way to access the data is via .data(),
|
||||
* which does not guarantee the termination that would be required by
|
||||
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
|
||||
* or strcasecmp would fail if it does not account for the length of the
|
||||
* view. Thus, contrary to the string/char* tests, this uses the same base
|
||||
* string but gets different sections to trigger these corner cases.
|
||||
*/
|
||||
std::string_view base{"aabAba"};
|
||||
|
||||
/* Everything ends with an empty prefix. */
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(0, 0), base.substr(1, 0))); // Different positions
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(0, 0)));
|
||||
|
||||
/* Equals string, ignoring case. */
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(1, 1))); // Different positions
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(3, 1)));
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(3, 1), base.substr(0, 1)));
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(3, 1), base.substr(3, 1)));
|
||||
|
||||
/* Ends with same, ignoring case. */
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(2, 2), base.substr(0, 1)));
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(2, 2), base.substr(3, 1)));
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(4, 2), base.substr(0, 1)));
|
||||
CHECK(StrEndsWithIgnoreCase(base.substr(4, 2), base.substr(3, 1)));
|
||||
|
||||
/* Does not end the same. */
|
||||
CHECK(!StrEndsWithIgnoreCase(base.substr(2, 0), base.substr(2, 1)));
|
||||
CHECK(!StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(2, 1)));
|
||||
CHECK(!StrEndsWithIgnoreCase(base.substr(2, 1), base.substr(0, 1)));
|
||||
CHECK(!StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(0, 2)));
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("FormatArrayAsHex")
|
||||
{
|
||||
CHECK(FormatArrayAsHex(std::array<byte, 0>{}) == "");
|
||||
CHECK(FormatArrayAsHex(std::array<byte, 1>{0x12}) == "12");
|
||||
CHECK(FormatArrayAsHex(std::array<byte, 4>{0x13, 0x38, 0x42, 0xAF}) == "133842af");
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file strings_func.cpp Test functionality from strings_func. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../3rdparty/catch2/catch.hpp"
|
||||
|
||||
#include "../strings_func.h"
|
||||
|
||||
TEST_CASE("HaveDParamChanged")
|
||||
{
|
||||
SetDParam(0, 0);
|
||||
SetDParamStr(1, "some string");
|
||||
|
||||
std::vector<StringParameterBackup> backup;
|
||||
CopyOutDParam(backup, 2);
|
||||
|
||||
CHECK(HaveDParamChanged(backup) == false);
|
||||
|
||||
/* A different parameter 0 (both string and numeric). */
|
||||
SetDParam(0, 1);
|
||||
CHECK(HaveDParamChanged(backup) == true);
|
||||
|
||||
SetDParamStr(0, "some other string");
|
||||
CHECK(HaveDParamChanged(backup) == true);
|
||||
|
||||
/* Back to the original state, nothing should have changed. */
|
||||
SetDParam(0, 0);
|
||||
CHECK(HaveDParamChanged(backup) == false);
|
||||
|
||||
/* A different parameter 1 (both string and numeric). */
|
||||
SetDParamStr(1, "some other string");
|
||||
CHECK(HaveDParamChanged(backup) == true);
|
||||
|
||||
SetDParam(1, 0);
|
||||
CHECK(HaveDParamChanged(backup) == true);
|
||||
|
||||
/* Back to the original state, nothing should have changed. */
|
||||
SetDParamStr(1, "some string");
|
||||
CHECK(HaveDParamChanged(backup) == false);
|
||||
|
||||
/* Changing paramter 2 should not have any effect, as the backup is only 2 long. */
|
||||
SetDParam(2, 3);
|
||||
CHECK(HaveDParamChanged(backup) == false);
|
||||
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file script_admin_json.cpp Tests for the Squirrel -> JSON conversion in ScriptAdmin. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../3rdparty/catch2/catch.hpp"
|
||||
|
||||
#include "../game/game_instance.hpp"
|
||||
#include "../script/api/script_admin.hpp"
|
||||
#include "../script/api/script_event_types.hpp"
|
||||
#include "../script/script_instance.hpp"
|
||||
#include "../script/squirrel.hpp"
|
||||
#include "../core/format.hpp"
|
||||
|
||||
#include "../3rdparty/nlohmann/json.hpp"
|
||||
|
||||
#include <squirrel.h>
|
||||
|
||||
/**
|
||||
* A controller to start enough so we can use Squirrel for testing.
|
||||
*
|
||||
* To run Squirrel, we need an Allocator, so malloc/free works.
|
||||
* For functions that log, we need an ActiveInstance, so the logger knows where
|
||||
* to send the logs to.
|
||||
*
|
||||
* By instantiating this class, both are set correctly. After that you can
|
||||
* use Squirrel without issues.
|
||||
*/
|
||||
class TestScriptController {
|
||||
public:
|
||||
GameInstance game{};
|
||||
ScriptObject::ActiveInstance active{&game};
|
||||
|
||||
Squirrel engine{"test"};
|
||||
ScriptAllocatorScope scope{&engine};
|
||||
};
|
||||
|
||||
extern bool ScriptAdminMakeJSON(nlohmann::json &json, HSQUIRRELVM vm, SQInteger index, int depth = 0);
|
||||
|
||||
/**
|
||||
* Small wrapper around ScriptAdmin's MakeJSON that prepares the Squirrel
|
||||
* engine if it was called from actual scripting..
|
||||
*/
|
||||
static std::optional<std::string> TestScriptAdminMakeJSON(std::string_view squirrel)
|
||||
{
|
||||
auto vm = sq_open(1024);
|
||||
/* sq_compile creates a closure with our snipper, which is a table.
|
||||
* Add "return " to get the table on the stack. */
|
||||
std::string buffer = fmt::format("return {}", squirrel);
|
||||
|
||||
/* Insert an (empty) class for testing. */
|
||||
sq_pushroottable(vm);
|
||||
sq_pushstring(vm, "DummyClass", -1);
|
||||
sq_newclass(vm, SQFalse);
|
||||
sq_newslot(vm, -3, SQFalse);
|
||||
sq_pop(vm, 1);
|
||||
|
||||
/* Compile the snippet. */
|
||||
REQUIRE(sq_compilebuffer(vm, buffer.c_str(), buffer.size(), "test", SQTrue) == SQ_OK);
|
||||
/* Execute the snippet, capturing the return value. */
|
||||
sq_pushroottable(vm);
|
||||
REQUIRE(sq_call(vm, 1, SQTrue, SQTrue) == SQ_OK);
|
||||
/* Ensure the snippet pushed a table on the stack. */
|
||||
REQUIRE(sq_gettype(vm, -1) == OT_TABLE);
|
||||
|
||||
/* Feed the snippet into the MakeJSON function. */
|
||||
nlohmann::json json;
|
||||
if (!ScriptAdminMakeJSON(json, vm, -1)) {
|
||||
sq_close(vm);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
sq_close(vm);
|
||||
return json.dump();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate ScriptEventAdminPort can convert JSON to Squirrel.
|
||||
*
|
||||
* This function is not actually part of ScriptAdmin, but we will use MakeJSON,
|
||||
* and as such need to be inside this class.
|
||||
*
|
||||
* The easiest way to do validate, is to first use ScriptEventAdminPort (the function
|
||||
* we are testing) to convert the JSON to a Squirrel table. Then to use MakeJSON
|
||||
* to convert it back to JSON.
|
||||
*
|
||||
* Sadly, Squirrel has no way to easily compare if two tables are identical, so we
|
||||
* use the JSON -> Squirrel -> JSON method to validate the conversion. But mind you,
|
||||
* a failure in the final JSON might also mean a bug in MakeJSON.
|
||||
*
|
||||
* @param json The JSON-string to convert to Squirrel
|
||||
* @return The Squirrel table converted to a JSON-string.
|
||||
*/
|
||||
static std::optional<std::string> TestScriptEventAdminPort(const std::string &json)
|
||||
{
|
||||
auto vm = sq_open(1024);
|
||||
|
||||
/* Run the conversion JSON -> Squirrel (this will now be on top of the stack). */
|
||||
ScriptEventAdminPort(json).GetObject(vm);
|
||||
if (sq_gettype(vm, -1) == OT_NULL) {
|
||||
sq_close(vm);
|
||||
return std::nullopt;
|
||||
}
|
||||
REQUIRE(sq_gettype(vm, -1) == OT_TABLE);
|
||||
|
||||
nlohmann::json squirrel_json;
|
||||
REQUIRE(ScriptAdminMakeJSON(squirrel_json, vm, -1) == true);
|
||||
|
||||
sq_close(vm);
|
||||
return squirrel_json.dump();
|
||||
}
|
||||
|
||||
TEST_CASE("Squirrel -> JSON conversion")
|
||||
{
|
||||
TestScriptController controller;
|
||||
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = null })sq") == R"json({"test":null})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = 1 })sq") == R"json({"test":1})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = -1 })sq") == R"json({"test":-1})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = true })sq") == R"json({"test":true})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = "a" })sq") == R"json({"test":"a"})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = [ ] })sq") == R"json({"test":[]})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = [ 1 ] })sq") == R"json({"test":[1]})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = [ 1, "a", true, { test = 1 }, [], null ] })sq") == R"json({"test":[1,"a",true,{"test":1},[],null]})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = { } })sq") == R"json({"test":{}})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = { test = 1 } })sq") == R"json({"test":{"test":1}})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = { test = 1, test = 2 } })sq") == R"json({"test":{"test":2}})json");
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = { test = 1, test2 = [ 2 ] } })sq") == R"json({"test":{"test":1,"test2":[2]}})json");
|
||||
|
||||
/* Cases that should fail, as we cannot convert a class to JSON. */
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = DummyClass })sq") == std::nullopt);
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = [ 1, DummyClass ] })sq") == std::nullopt);
|
||||
CHECK(TestScriptAdminMakeJSON(R"sq({ test = { test = 1, test2 = DummyClass } })sq") == std::nullopt);
|
||||
}
|
||||
|
||||
TEST_CASE("JSON -> Squirrel conversion")
|
||||
{
|
||||
TestScriptController controller;
|
||||
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": null })json") == R"json({"test":null})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": 1 })json") == R"json({"test":1})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": -1 })json") == R"json({"test":-1})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": true })json") == R"json({"test":true})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": "a" })json") == R"json({"test":"a"})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": [] })json") == R"json({"test":[]})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": [ 1 ] })json") == R"json({"test":[1]})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": [ 1, "a", true, { "test": 1 }, [], null ] })json") == R"json({"test":[1,"a",true,{"test":1},[],null]})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": {} })json") == R"json({"test":{}})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": { "test": 1 } })json") == R"json({"test":{"test":1}})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": { "test": 2 } })json") == R"json({"test":{"test":2}})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": { "test": 1, "test2": [ 2 ] } })json") == R"json({"test":{"test":1,"test2":[2]}})json");
|
||||
|
||||
/* Check if spaces are properly ignored. */
|
||||
CHECK(TestScriptEventAdminPort(R"json({"test":1})json") == R"json({"test":1})json");
|
||||
CHECK(TestScriptEventAdminPort(R"json({"test": 1})json") == R"json({"test":1})json");
|
||||
|
||||
/* Valid JSON but invalid Squirrel (read: floats). */
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": 1.1 })json") == std::nullopt);
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": [ 1, 3, 1.1 ] })json") == std::nullopt);
|
||||
|
||||
/* Root element has to be an object. */
|
||||
CHECK(TestScriptEventAdminPort(R"json( 1 )json") == std::nullopt);
|
||||
CHECK(TestScriptEventAdminPort(R"json( "a" )json") == std::nullopt);
|
||||
CHECK(TestScriptEventAdminPort(R"json( [ 1 ] )json") == std::nullopt);
|
||||
CHECK(TestScriptEventAdminPort(R"json( null )json") == std::nullopt);
|
||||
CHECK(TestScriptEventAdminPort(R"json( true )json") == std::nullopt);
|
||||
|
||||
/* Cases that should fail, as it is invalid JSON. */
|
||||
CHECK(TestScriptEventAdminPort(R"json({"test":test})json") == std::nullopt);
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": 1 )json") == std::nullopt); // Missing closing }
|
||||
CHECK(TestScriptEventAdminPort(R"json( "test": 1})json") == std::nullopt); // Missing opening {
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test" = 1})json") == std::nullopt);
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": [ 1 })json") == std::nullopt); // Missing closing ]
|
||||
CHECK(TestScriptEventAdminPort(R"json({ "test": 1 ] })json") == std::nullopt); // Missing opening [
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file test_window_desc.cpp Test WindowDescs for valid widget parts. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../3rdparty/catch2/catch.hpp"
|
||||
|
||||
#include "mock_environment.h"
|
||||
|
||||
#include "../window_gui.h"
|
||||
#include "../core/format.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
/**
|
||||
* List of WindowDescs. Defined in window.cpp but not exposed as this unit-test is the only other place that needs it.
|
||||
* WindowDesc is a self-registering class so all WindowDescs will be included in the list.
|
||||
*/
|
||||
extern std::vector<WindowDesc*> *_window_descs;
|
||||
|
||||
|
||||
class WindowDescTestsFixture {
|
||||
private:
|
||||
MockEnvironment &mock = MockEnvironment::Instance();
|
||||
};
|
||||
|
||||
|
||||
TEST_CASE("WindowDesc - ini_key uniqueness")
|
||||
{
|
||||
std::set<std::string> seen;
|
||||
|
||||
for (const WindowDesc *window_desc : *_window_descs) {
|
||||
|
||||
if (window_desc->ini_key == nullptr) continue;
|
||||
|
||||
CAPTURE(window_desc->ini_key);
|
||||
CHECK((seen.find(window_desc->ini_key) == std::end(seen)));
|
||||
|
||||
seen.insert(window_desc->ini_key);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("WindowDesc - ini_key validity")
|
||||
{
|
||||
const WindowDesc *window_desc = GENERATE(from_range(std::begin(*_window_descs), std::end(*_window_descs)));
|
||||
|
||||
bool has_inikey = window_desc->ini_key != nullptr;
|
||||
bool has_widget = std::any_of(window_desc->nwid_begin, window_desc->nwid_end, [](const NWidgetPart &part) { return part.type == WWT_DEFSIZEBOX || part.type == WWT_STICKYBOX; });
|
||||
|
||||
INFO(fmt::format("{}:{}", window_desc->file, window_desc->line));
|
||||
CAPTURE(has_inikey);
|
||||
CAPTURE(has_widget);
|
||||
|
||||
CHECK((has_widget == has_inikey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a NWidgetTree is properly closed, meaning the number of container-type parts matches the number of
|
||||
* EndContainer() parts.
|
||||
* @param nwid_begin Pointer to beginning of nested widget parts.
|
||||
* @param nwid_end Pointer to ending of nested widget parts.
|
||||
* @return True iff nested tree is properly closed.
|
||||
*/
|
||||
static bool IsNWidgetTreeClosed(const NWidgetPart *nwid_begin, const NWidgetPart *nwid_end)
|
||||
{
|
||||
int depth = 0;
|
||||
for (; nwid_begin < nwid_end; ++nwid_begin) {
|
||||
if (IsContainerWidgetType(nwid_begin->type)) ++depth;
|
||||
if (nwid_begin->type == WPT_ENDCONTAINER) --depth;
|
||||
}
|
||||
return depth == 0;
|
||||
}
|
||||
|
||||
TEST_CASE("WindowDesc - NWidgetParts properly closed")
|
||||
{
|
||||
const WindowDesc *window_desc = GENERATE(from_range(std::begin(*_window_descs), std::end(*_window_descs)));
|
||||
|
||||
INFO(fmt::format("{}:{}", window_desc->file, window_desc->line));
|
||||
|
||||
CHECK(IsNWidgetTreeClosed(window_desc->nwid_begin, window_desc->nwid_end));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(WindowDescTestsFixture, "WindowDesc - NWidgetPart validity")
|
||||
{
|
||||
const WindowDesc *window_desc = GENERATE(from_range(std::begin(*_window_descs), std::end(*_window_descs)));
|
||||
|
||||
INFO(fmt::format("{}:{}", window_desc->file, window_desc->line));
|
||||
|
||||
int biggest_index = -1;
|
||||
NWidgetStacked *shade_select = nullptr;
|
||||
NWidgetBase *root = nullptr;
|
||||
|
||||
REQUIRE_NOTHROW(root = MakeWindowNWidgetTree(window_desc->nwid_begin, window_desc->nwid_end, &biggest_index, &shade_select));
|
||||
CHECK((root != nullptr));
|
||||
}
|
Loading…
Reference in New Issue