summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/test
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/utils/test/CMakeLists.txt51
-rw-r--r--xbmc/utils/test/CXBMCTinyXML-test.xml6
-rw-r--r--xbmc/utils/test/TestAlarmClock.cpp25
-rw-r--r--xbmc/utils/test/TestAliasShortcutUtils.cpp91
-rw-r--r--xbmc/utils/test/TestArchive.cpp411
-rw-r--r--xbmc/utils/test/TestBase64.cpp77
-rw-r--r--xbmc/utils/test/TestBitstreamStats.cpp60
-rw-r--r--xbmc/utils/test/TestCPUInfo.cpp72
-rw-r--r--xbmc/utils/test/TestCharsetConverter.cpp401
-rw-r--r--xbmc/utils/test/TestComponentContainer.cpp76
-rw-r--r--xbmc/utils/test/TestCrc32.cpp50
-rw-r--r--xbmc/utils/test/TestDatabaseUtils.cpp1377
-rw-r--r--xbmc/utils/test/TestDigest.cpp99
-rw-r--r--xbmc/utils/test/TestEndianSwap.cpp133
-rw-r--r--xbmc/utils/test/TestExecString.cpp118
-rw-r--r--xbmc/utils/test/TestFileOperationJob.cpp292
-rw-r--r--xbmc/utils/test/TestFileUtils.cpp44
-rw-r--r--xbmc/utils/test/TestGlobalsHandling.cpp25
-rw-r--r--xbmc/utils/test/TestGlobalsHandlingPattern1.h40
-rw-r--r--xbmc/utils/test/TestHTMLUtil.cpp36
-rw-r--r--xbmc/utils/test/TestHttpHeader.cpp505
-rw-r--r--xbmc/utils/test/TestHttpParser.cpp49
-rw-r--r--xbmc/utils/test/TestHttpRangeUtils.cpp887
-rw-r--r--xbmc/utils/test/TestHttpResponse.cpp43
-rw-r--r--xbmc/utils/test/TestJSONVariantParser.cpp189
-rw-r--r--xbmc/utils/test/TestJSONVariantWriter.cpp151
-rw-r--r--xbmc/utils/test/TestJobManager.cpp218
-rw-r--r--xbmc/utils/test/TestLabelFormatter.cpp69
-rw-r--r--xbmc/utils/test/TestLangCodeExpander.cpp29
-rw-r--r--xbmc/utils/test/TestLocale.cpp272
-rw-r--r--xbmc/utils/test/TestMathUtils.cpp59
-rw-r--r--xbmc/utils/test/TestMime.cpp29
-rw-r--r--xbmc/utils/test/TestPOUtils.cpp47
-rw-r--r--xbmc/utils/test/TestRegExp.cpp169
-rw-r--r--xbmc/utils/test/TestRingBuffer.cpp33
-rw-r--r--xbmc/utils/test/TestScraperParser.cpp24
-rw-r--r--xbmc/utils/test/TestScraperUrl.cpp34
-rw-r--r--xbmc/utils/test/TestSortUtils.cpp123
-rw-r--r--xbmc/utils/test/TestStopwatch.cpp66
-rw-r--r--xbmc/utils/test/TestStreamDetails.cpp76
-rw-r--r--xbmc/utils/test/TestStreamUtils.cpp23
-rw-r--r--xbmc/utils/test/TestStringUtils.cpp609
-rw-r--r--xbmc/utils/test/TestSystemInfo.cpp326
-rw-r--r--xbmc/utils/test/TestURIUtils.cpp585
-rw-r--r--xbmc/utils/test/TestUrlOptions.cpp193
-rw-r--r--xbmc/utils/test/TestVariant.cpp334
-rw-r--r--xbmc/utils/test/TestXBMCTinyXML.cpp58
-rw-r--r--xbmc/utils/test/TestXMLUtils.cpp356
-rw-r--r--xbmc/utils/test/Testlog.cpp96
-rw-r--r--xbmc/utils/test/Testrfft.cpp41
-rw-r--r--xbmc/utils/test/data/language/Spanish/strings.po26
51 files changed, 9203 insertions, 0 deletions
diff --git a/xbmc/utils/test/CMakeLists.txt b/xbmc/utils/test/CMakeLists.txt
new file mode 100644
index 0000000..a5ba095
--- /dev/null
+++ b/xbmc/utils/test/CMakeLists.txt
@@ -0,0 +1,51 @@
+set(SOURCES TestAlarmClock.cpp
+ TestAliasShortcutUtils.cpp
+ TestArchive.cpp
+ TestBase64.cpp
+ TestBitstreamStats.cpp
+ TestCharsetConverter.cpp
+ TestCPUInfo.cpp
+ TestComponentContainer.cpp
+ TestCrc32.cpp
+ TestDatabaseUtils.cpp
+ TestDigest.cpp
+ TestEndianSwap.cpp
+ TestExecString.cpp
+ TestFileOperationJob.cpp
+ TestFileUtils.cpp
+ TestGlobalsHandling.cpp
+ TestHTMLUtil.cpp
+ TestHttpHeader.cpp
+ TestHttpParser.cpp
+ TestHttpRangeUtils.cpp
+ TestHttpResponse.cpp
+ TestJobManager.cpp
+ TestJSONVariantParser.cpp
+ TestJSONVariantWriter.cpp
+ TestLabelFormatter.cpp
+ TestLangCodeExpander.cpp
+ TestLocale.cpp
+ Testlog.cpp
+ TestMathUtils.cpp
+ TestMime.cpp
+ TestPOUtils.cpp
+ TestRegExp.cpp
+ Testrfft.cpp
+ TestRingBuffer.cpp
+ TestScraperParser.cpp
+ TestScraperUrl.cpp
+ TestSortUtils.cpp
+ TestStopwatch.cpp
+ TestStreamDetails.cpp
+ TestStreamUtils.cpp
+ TestStringUtils.cpp
+ TestSystemInfo.cpp
+ TestURIUtils.cpp
+ TestUrlOptions.cpp
+ TestVariant.cpp
+ TestXBMCTinyXML.cpp
+ TestXMLUtils.cpp)
+
+set(HEADERS TestGlobalsHandlingPattern1.h)
+
+core_add_test_library(utils_test)
diff --git a/xbmc/utils/test/CXBMCTinyXML-test.xml b/xbmc/utils/test/CXBMCTinyXML-test.xml
new file mode 100644
index 0000000..9444dc8
--- /dev/null
+++ b/xbmc/utils/test/CXBMCTinyXML-test.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<details>
+ <url function="ParseTMDBRating" cache="tmdb-en-12244.json">
+ http://api.themoviedb.org/3/movie/12244?api_key=57983e31fb435df4df77afb854740ea9&language=en&#x3f;&#x003F;&#0063;
+ </url>
+</details>
diff --git a/xbmc/utils/test/TestAlarmClock.cpp b/xbmc/utils/test/TestAlarmClock.cpp
new file mode 100644
index 0000000..75ea84a
--- /dev/null
+++ b/xbmc/utils/test/TestAlarmClock.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/AlarmClock.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestAlarmClock, General)
+{
+ CAlarmClock a;
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_FALSE(a.HasAlarm("test"));
+ a.Start("test", 100.f, "test");
+ EXPECT_TRUE(a.IsRunning());
+ EXPECT_TRUE(a.HasAlarm("test"));
+ EXPECT_FALSE(a.HasAlarm("test2"));
+ EXPECT_NE(0.f, a.GetRemaining("test"));
+ EXPECT_EQ(0.f, a.GetRemaining("test2"));
+ a.Stop("test");
+}
diff --git a/xbmc/utils/test/TestAliasShortcutUtils.cpp b/xbmc/utils/test/TestAliasShortcutUtils.cpp
new file mode 100644
index 0000000..d36fd41
--- /dev/null
+++ b/xbmc/utils/test/TestAliasShortcutUtils.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/AliasShortcutUtils.h"
+#include "filesystem/File.h"
+#include "test/TestUtils.h"
+
+#if defined(TARGET_DARWIN_OSX)
+#include "platform/darwin/DarwinUtils.h"
+#endif
+#include <gtest/gtest.h>
+
+TEST(TestAliasShortcutUtils, IsAliasShortcut)
+{
+ XFILE::CFile *tmpFile = XBMC_CREATETEMPFILE("noaliastest");
+ std::string noalias = XBMC_TEMPFILEPATH(tmpFile);
+
+#if defined(TARGET_DARWIN_OSX)
+ XFILE::CFile *aliasDestFile = XBMC_CREATETEMPFILE("aliastest");
+ std::string alias = XBMC_TEMPFILEPATH(aliasDestFile);
+
+ //we only need the path here so delete the alias file
+ //which will be recreated as shortcut later:
+ XBMC_DELETETEMPFILE(aliasDestFile);
+
+ // create alias from a pointing to /Volumes
+ CDarwinUtils::CreateAliasShortcut(alias, "/Volumes");
+ EXPECT_TRUE(IsAliasShortcut(alias, true));
+ XFILE::CFile::Delete(alias);
+
+ // volumes is not a shortcut but a dir
+ EXPECT_FALSE(IsAliasShortcut("/Volumes", true));
+#endif
+
+ // a regular file is not a shortcut
+ EXPECT_FALSE(IsAliasShortcut(noalias, false));
+ XBMC_DELETETEMPFILE(tmpFile);
+
+ // empty string is not an alias
+ std::string emptyString;
+ EXPECT_FALSE(IsAliasShortcut(emptyString, false));
+
+ // non-existent file is no alias
+ std::string nonExistingFile="/IDontExistsNormally/somefile.txt";
+ EXPECT_FALSE(IsAliasShortcut(nonExistingFile, false));
+}
+
+TEST(TestAliasShortcutUtils, TranslateAliasShortcut)
+{
+ XFILE::CFile *tmpFile = XBMC_CREATETEMPFILE("noaliastest");
+ std::string noalias = XBMC_TEMPFILEPATH(tmpFile);
+ std::string noaliastemp = noalias;
+
+#if defined(TARGET_DARWIN_OSX)
+ XFILE::CFile *aliasDestFile = XBMC_CREATETEMPFILE("aliastest");
+ std::string alias = XBMC_TEMPFILEPATH(aliasDestFile);
+
+ //we only need the path here so delete the alias file
+ //which will be recreated as shortcut later:
+ XBMC_DELETETEMPFILE(aliasDestFile);
+
+ // create alias from a pointing to /Volumes
+ CDarwinUtils::CreateAliasShortcut(alias, "/Volumes");
+
+ // resolve the shortcut
+ TranslateAliasShortcut(alias);
+ EXPECT_STREQ("/Volumes", alias.c_str());
+ XFILE::CFile::Delete(alias);
+#endif
+
+ // translating a non-shortcut url should result in no change...
+ TranslateAliasShortcut(noaliastemp);
+ EXPECT_STREQ(noaliastemp.c_str(), noalias.c_str());
+ XBMC_DELETETEMPFILE(tmpFile);
+
+ //translate empty should stay empty
+ std::string emptyString;
+ TranslateAliasShortcut(emptyString);
+ EXPECT_STREQ("", emptyString.c_str());
+
+ // translate non-existent file should result in no change...
+ std::string nonExistingFile="/IDontExistsNormally/somefile.txt";
+ std::string resolvedNonExistingFile=nonExistingFile;
+ TranslateAliasShortcut(resolvedNonExistingFile);
+ EXPECT_STREQ(resolvedNonExistingFile.c_str(), nonExistingFile.c_str());
+}
diff --git a/xbmc/utils/test/TestArchive.cpp b/xbmc/utils/test/TestArchive.cpp
new file mode 100644
index 0000000..90628ea
--- /dev/null
+++ b/xbmc/utils/test/TestArchive.cpp
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#if defined(TARGET_WINDOWS)
+# include <windows.h>
+#endif
+
+#include "filesystem/File.h"
+#include "test/TestUtils.h"
+#include "utils/Archive.h"
+#include "utils/Variant.h"
+#include "utils/XTimeUtils.h"
+
+#include <gtest/gtest.h>
+
+class TestArchive : public testing::Test
+{
+protected:
+ TestArchive()
+ {
+ file = XBMC_CREATETEMPFILE(".ar");
+ }
+ ~TestArchive() override
+ {
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(file));
+ }
+ XFILE::CFile *file;
+};
+
+TEST_F(TestArchive, IsStoring)
+{
+ ASSERT_NE(nullptr, file);
+ CArchive arstore(file, CArchive::store);
+ EXPECT_TRUE(arstore.IsStoring());
+ EXPECT_FALSE(arstore.IsLoading());
+ arstore.Close();
+}
+
+TEST_F(TestArchive, IsLoading)
+{
+ ASSERT_NE(nullptr, file);
+ CArchive arload(file, CArchive::load);
+ EXPECT_TRUE(arload.IsLoading());
+ EXPECT_FALSE(arload.IsStoring());
+ arload.Close();
+}
+
+TEST_F(TestArchive, FloatArchive)
+{
+ ASSERT_NE(nullptr, file);
+ float float_ref = 1, float_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << float_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> float_var;
+ arload.Close();
+
+ EXPECT_EQ(float_ref, float_var);
+}
+
+TEST_F(TestArchive, DoubleArchive)
+{
+ ASSERT_NE(nullptr, file);
+ double double_ref = 2, double_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << double_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> double_var;
+ arload.Close();
+
+ EXPECT_EQ(double_ref, double_var);
+}
+
+TEST_F(TestArchive, IntegerArchive)
+{
+ ASSERT_NE(nullptr, file);
+ int int_ref = 3, int_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << int_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> int_var;
+ arload.Close();
+
+ EXPECT_EQ(int_ref, int_var);
+}
+
+TEST_F(TestArchive, UnsignedIntegerArchive)
+{
+ ASSERT_NE(nullptr, file);
+ unsigned int unsigned_int_ref = 4, unsigned_int_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << unsigned_int_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> unsigned_int_var;
+ arload.Close();
+
+ EXPECT_EQ(unsigned_int_ref, unsigned_int_var);
+}
+
+TEST_F(TestArchive, Int64tArchive)
+{
+ ASSERT_NE(nullptr, file);
+ int64_t int64_t_ref = 5, int64_t_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << int64_t_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> int64_t_var;
+ arload.Close();
+
+ EXPECT_EQ(int64_t_ref, int64_t_var);
+}
+
+TEST_F(TestArchive, UInt64tArchive)
+{
+ ASSERT_NE(nullptr, file);
+ uint64_t uint64_t_ref = 6, uint64_t_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << uint64_t_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> uint64_t_var;
+ arload.Close();
+
+ EXPECT_EQ(uint64_t_ref, uint64_t_var);
+}
+
+TEST_F(TestArchive, BoolArchive)
+{
+ ASSERT_NE(nullptr, file);
+ bool bool_ref = true, bool_var = false;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << bool_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> bool_var;
+ arload.Close();
+
+ EXPECT_EQ(bool_ref, bool_var);
+}
+
+TEST_F(TestArchive, CharArchive)
+{
+ ASSERT_NE(nullptr, file);
+ char char_ref = 'A', char_var = '\0';
+
+ CArchive arstore(file, CArchive::store);
+ arstore << char_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> char_var;
+ arload.Close();
+
+ EXPECT_EQ(char_ref, char_var);
+}
+
+TEST_F(TestArchive, WStringArchive)
+{
+ ASSERT_NE(nullptr, file);
+ std::wstring wstring_ref = L"test wstring", wstring_var;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << wstring_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> wstring_var;
+ arload.Close();
+
+ EXPECT_STREQ(wstring_ref.c_str(), wstring_var.c_str());
+}
+
+TEST_F(TestArchive, StringArchive)
+{
+ ASSERT_NE(nullptr, file);
+ std::string string_ref = "test string", string_var;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << string_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> string_var;
+ arload.Close();
+
+ EXPECT_STREQ(string_ref.c_str(), string_var.c_str());
+}
+
+TEST_F(TestArchive, SystemTimeArchive)
+{
+ ASSERT_NE(nullptr, file);
+ KODI::TIME::SystemTime SystemTime_ref = {1, 2, 3, 4, 5, 6, 7, 8};
+ KODI::TIME::SystemTime SystemTime_var = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ CArchive arstore(file, CArchive::store);
+ arstore << SystemTime_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> SystemTime_var;
+ arload.Close();
+
+ EXPECT_TRUE(!memcmp(&SystemTime_ref, &SystemTime_var, sizeof(KODI::TIME::SystemTime)));
+}
+
+TEST_F(TestArchive, CVariantArchive)
+{
+ ASSERT_NE(nullptr, file);
+ CVariant CVariant_ref((int)1), CVariant_var;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << CVariant_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> CVariant_var;
+ arload.Close();
+
+ EXPECT_TRUE(CVariant_var.isInteger());
+ EXPECT_EQ(1, CVariant_var.asInteger());
+}
+
+TEST_F(TestArchive, CVariantArchiveString)
+{
+ ASSERT_NE(nullptr, file);
+ CVariant CVariant_ref("teststring"), CVariant_var;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << CVariant_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> CVariant_var;
+ arload.Close();
+
+ EXPECT_TRUE(CVariant_var.isString());
+ EXPECT_STREQ("teststring", CVariant_var.asString().c_str());
+}
+
+TEST_F(TestArchive, StringVectorArchive)
+{
+ ASSERT_NE(nullptr, file);
+ std::vector<std::string> strArray_ref, strArray_var;
+ strArray_ref.emplace_back("test strArray_ref 0");
+ strArray_ref.emplace_back("test strArray_ref 1");
+ strArray_ref.emplace_back("test strArray_ref 2");
+ strArray_ref.emplace_back("test strArray_ref 3");
+
+ CArchive arstore(file, CArchive::store);
+ arstore << strArray_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> strArray_var;
+ arload.Close();
+
+ EXPECT_STREQ("test strArray_ref 0", strArray_var.at(0).c_str());
+ EXPECT_STREQ("test strArray_ref 1", strArray_var.at(1).c_str());
+ EXPECT_STREQ("test strArray_ref 2", strArray_var.at(2).c_str());
+ EXPECT_STREQ("test strArray_ref 3", strArray_var.at(3).c_str());
+}
+
+TEST_F(TestArchive, IntegerVectorArchive)
+{
+ ASSERT_NE(nullptr, file);
+ std::vector<int> iArray_ref, iArray_var;
+ iArray_ref.push_back(0);
+ iArray_ref.push_back(1);
+ iArray_ref.push_back(2);
+ iArray_ref.push_back(3);
+
+ CArchive arstore(file, CArchive::store);
+ arstore << iArray_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ arload >> iArray_var;
+ arload.Close();
+
+ EXPECT_EQ(0, iArray_var.at(0));
+ EXPECT_EQ(1, iArray_var.at(1));
+ EXPECT_EQ(2, iArray_var.at(2));
+ EXPECT_EQ(3, iArray_var.at(3));
+}
+
+TEST_F(TestArchive, MultiTypeArchive)
+{
+ ASSERT_NE(nullptr, file);
+ float float_ref = 1, float_var = 0;
+ double double_ref = 2, double_var = 0;
+ int int_ref = 3, int_var = 0;
+ unsigned int unsigned_int_ref = 4, unsigned_int_var = 0;
+ int64_t int64_t_ref = 5, int64_t_var = 0;
+ uint64_t uint64_t_ref = 6, uint64_t_var = 0;
+ bool bool_ref = true, bool_var = false;
+ char char_ref = 'A', char_var = '\0';
+ std::string string_ref = "test string", string_var;
+ std::wstring wstring_ref = L"test wstring", wstring_var;
+ KODI::TIME::SystemTime SystemTime_ref = {1, 2, 3, 4, 5, 6, 7, 8};
+ KODI::TIME::SystemTime SystemTime_var = {0, 0, 0, 0, 0, 0, 0, 0};
+ CVariant CVariant_ref((int)1), CVariant_var;
+ std::vector<std::string> strArray_ref, strArray_var;
+ strArray_ref.emplace_back("test strArray_ref 0");
+ strArray_ref.emplace_back("test strArray_ref 1");
+ strArray_ref.emplace_back("test strArray_ref 2");
+ strArray_ref.emplace_back("test strArray_ref 3");
+ std::vector<int> iArray_ref, iArray_var;
+ iArray_ref.push_back(0);
+ iArray_ref.push_back(1);
+ iArray_ref.push_back(2);
+ iArray_ref.push_back(3);
+
+ CArchive arstore(file, CArchive::store);
+ EXPECT_TRUE(arstore.IsStoring());
+ EXPECT_FALSE(arstore.IsLoading());
+ arstore << float_ref;
+ arstore << double_ref;
+ arstore << int_ref;
+ arstore << unsigned_int_ref;
+ arstore << int64_t_ref;
+ arstore << uint64_t_ref;
+ arstore << bool_ref;
+ arstore << char_ref;
+ arstore << string_ref;
+ arstore << wstring_ref;
+ arstore << SystemTime_ref;
+ arstore << CVariant_ref;
+ arstore << strArray_ref;
+ arstore << iArray_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+ EXPECT_TRUE(arload.IsLoading());
+ EXPECT_FALSE(arload.IsStoring());
+ arload >> float_var;
+ arload >> double_var;
+ arload >> int_var;
+ arload >> unsigned_int_var;
+ arload >> int64_t_var;
+ arload >> uint64_t_var;
+ arload >> bool_var;
+ arload >> char_var;
+ arload >> string_var;
+ arload >> wstring_var;
+ arload >> SystemTime_var;
+ arload >> CVariant_var;
+ arload >> strArray_var;
+ arload >> iArray_var;
+ arload.Close();
+
+ EXPECT_EQ(float_ref, float_var);
+ EXPECT_EQ(double_ref, double_var);
+ EXPECT_EQ(int_ref, int_var);
+ EXPECT_EQ(unsigned_int_ref, unsigned_int_var);
+ EXPECT_EQ(int64_t_ref, int64_t_var);
+ EXPECT_EQ(uint64_t_ref, uint64_t_var);
+ EXPECT_EQ(bool_ref, bool_var);
+ EXPECT_EQ(char_ref, char_var);
+ EXPECT_STREQ(string_ref.c_str(), string_var.c_str());
+ EXPECT_STREQ(wstring_ref.c_str(), wstring_var.c_str());
+ EXPECT_TRUE(!memcmp(&SystemTime_ref, &SystemTime_var, sizeof(KODI::TIME::SystemTime)));
+ EXPECT_TRUE(CVariant_var.isInteger());
+ EXPECT_STREQ("test strArray_ref 0", strArray_var.at(0).c_str());
+ EXPECT_STREQ("test strArray_ref 1", strArray_var.at(1).c_str());
+ EXPECT_STREQ("test strArray_ref 2", strArray_var.at(2).c_str());
+ EXPECT_STREQ("test strArray_ref 3", strArray_var.at(3).c_str());
+ EXPECT_EQ(0, iArray_var.at(0));
+ EXPECT_EQ(1, iArray_var.at(1));
+ EXPECT_EQ(2, iArray_var.at(2));
+ EXPECT_EQ(3, iArray_var.at(3));
+}
diff --git a/xbmc/utils/test/TestBase64.cpp b/xbmc/utils/test/TestBase64.cpp
new file mode 100644
index 0000000..8416378
--- /dev/null
+++ b/xbmc/utils/test/TestBase64.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/Base64.h"
+
+#include <gtest/gtest.h>
+
+static const char refdata[] = "\x01\x02\x03\x04\x05\x06\x07\x08"
+ "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+ "\x11\x12\x13\x14\x15\x16\x17\x18"
+ "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
+ "\x21\x22\x23\x24\x25\x26\x27\x28"
+ "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30";
+
+static const char refbase64data[] = "AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY"
+ "GRobHB0eHyAhIiMkJSYnKCkqKywtLi8w";
+
+TEST(TestBase64, Encode_1)
+{
+ std::string a;
+ Base64::Encode(refdata, sizeof(refdata) - 1, a);
+ EXPECT_STREQ(refbase64data, a.c_str());
+}
+
+TEST(TestBase64, Encode_2)
+{
+ std::string a;
+ a = Base64::Encode(refdata, sizeof(refdata) - 1);
+ EXPECT_STREQ(refbase64data, a.c_str());
+}
+
+TEST(TestBase64, Encode_3)
+{
+ std::string a;
+ Base64::Encode(refdata, a);
+ EXPECT_STREQ(refbase64data, a.c_str());
+}
+
+TEST(TestBase64, Encode_4)
+{
+ std::string a;
+ a = Base64::Encode(refdata);
+ EXPECT_STREQ(refbase64data, a.c_str());
+}
+
+TEST(TestBase64, Decode_1)
+{
+ std::string a;
+ Base64::Decode(refbase64data, sizeof(refbase64data) - 1, a);
+ EXPECT_STREQ(refdata, a.c_str());
+}
+
+TEST(TestBase64, Decode_2)
+{
+ std::string a;
+ a = Base64::Decode(refbase64data, sizeof(refbase64data) - 1);
+ EXPECT_STREQ(refdata, a.c_str());
+}
+
+TEST(TestBase64, Decode_3)
+{
+ std::string a;
+ Base64::Decode(refbase64data, a);
+ EXPECT_STREQ(refdata, a.c_str());
+}
+
+TEST(TestBase64, Decode_4)
+{
+ std::string a;
+ a = Base64::Decode(refbase64data);
+ EXPECT_STREQ(refdata, a.c_str());
+}
diff --git a/xbmc/utils/test/TestBitstreamStats.cpp b/xbmc/utils/test/TestBitstreamStats.cpp
new file mode 100644
index 0000000..200a633
--- /dev/null
+++ b/xbmc/utils/test/TestBitstreamStats.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "threads/Thread.h"
+#include "utils/BitstreamStats.h"
+
+#include <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+
+#define BITS (256 * 8)
+#define BYTES (256)
+
+class CTestBitstreamStatsThread : public CThread
+{
+public:
+ CTestBitstreamStatsThread() :
+ CThread("TestBitstreamStats"){}
+
+};
+
+TEST(TestBitstreamStats, General)
+{
+ int i;
+ BitstreamStats a;
+ CTestBitstreamStatsThread t;
+
+ i = 0;
+ a.Start();
+ EXPECT_EQ(0.0, a.GetBitrate());
+ EXPECT_EQ(0.0, a.GetMaxBitrate());
+ EXPECT_EQ(-1.0, a.GetMinBitrate());
+ while (i <= BITS)
+ {
+ a.AddSampleBits(1);
+ i++;
+ t.Sleep(1ms);
+ }
+ a.CalculateBitrate();
+ EXPECT_GT(a.GetBitrate(), 0.0);
+ EXPECT_GT(a.GetMaxBitrate(), 0.0);
+ EXPECT_GT(a.GetMinBitrate(), 0.0);
+
+ i = 0;
+ while (i <= BYTES)
+ {
+ a.AddSampleBytes(1);
+ t.Sleep(2ms);
+ i++;
+ }
+ a.CalculateBitrate();
+ EXPECT_GT(a.GetBitrate(), 0.0);
+ EXPECT_GT(a.GetMaxBitrate(), 0.0);
+ EXPECT_LE(a.GetMinBitrate(), a.GetMaxBitrate());
+}
diff --git a/xbmc/utils/test/TestCPUInfo.cpp b/xbmc/utils/test/TestCPUInfo.cpp
new file mode 100644
index 0000000..bd9572a
--- /dev/null
+++ b/xbmc/utils/test/TestCPUInfo.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#if defined(TARGET_WINDOWS)
+# include <windows.h>
+#endif
+
+#include "ServiceBroker.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/CPUInfo.h"
+#include "utils/Temperature.h"
+#include "utils/XTimeUtils.h"
+
+#include <gtest/gtest.h>
+
+struct TestCPUInfo : public ::testing::Test
+{
+ TestCPUInfo() { CServiceBroker::RegisterCPUInfo(CCPUInfo::GetCPUInfo()); }
+
+ ~TestCPUInfo() { CServiceBroker::UnregisterCPUInfo(); }
+};
+
+TEST_F(TestCPUInfo, GetUsedPercentage)
+{
+ EXPECT_GE(CServiceBroker::GetCPUInfo()->GetUsedPercentage(), 0);
+}
+
+TEST_F(TestCPUInfo, GetCPUCount)
+{
+ EXPECT_GT(CServiceBroker::GetCPUInfo()->GetCPUCount(), 0);
+}
+
+TEST_F(TestCPUInfo, GetCPUFrequency)
+{
+ EXPECT_GE(CServiceBroker::GetCPUInfo()->GetCPUFrequency(), 0.f);
+}
+
+#if defined(TARGET_WINDOWS)
+TEST_F(TestCPUInfo, DISABLED_GetTemperature)
+#else
+TEST_F(TestCPUInfo, GetTemperature)
+#endif
+{
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_cpuTempCmd = "echo '50 c'";
+ CTemperature t;
+ EXPECT_TRUE(CServiceBroker::GetCPUInfo()->GetTemperature(t));
+ EXPECT_TRUE(t.IsValid());
+}
+
+TEST_F(TestCPUInfo, CoreInfo)
+{
+ ASSERT_TRUE(CServiceBroker::GetCPUInfo()->HasCoreId(0));
+ const CoreInfo c = CServiceBroker::GetCPUInfo()->GetCoreInfo(0);
+ EXPECT_TRUE(c.m_id == 0);
+}
+
+TEST_F(TestCPUInfo, GetCoresUsageString)
+{
+ EXPECT_STRNE("", CServiceBroker::GetCPUInfo()->GetCoresUsageString().c_str());
+}
+
+TEST_F(TestCPUInfo, GetCPUFeatures)
+{
+ unsigned int a = CServiceBroker::GetCPUInfo()->GetCPUFeatures();
+ (void)a;
+}
diff --git a/xbmc/utils/test/TestCharsetConverter.cpp b/xbmc/utils/test/TestCharsetConverter.cpp
new file mode 100644
index 0000000..f8736b7
--- /dev/null
+++ b/xbmc/utils/test/TestCharsetConverter.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ServiceBroker.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/CharsetConverter.h"
+#include "utils/Utf8Utils.h"
+
+#include <gtest/gtest.h>
+
+#if 0
+static const uint16_t refutf16LE1[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff54, 0xff46,
+ 0xff11, 0xff16, 0xff2c, 0xff25,
+ 0xff54, 0xff4f, 0xff57, 0x0 };
+
+static const uint16_t refutf16LE2[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff54, 0xff46,
+ 0xff18, 0xff34, 0xff4f, 0xff1a,
+ 0xff3f, 0xff43, 0xff48, 0xff41,
+ 0xff52, 0xff53, 0xff45, 0xff54,
+ 0xff3f, 0xff35, 0xff34, 0xff26,
+ 0xff0d, 0xff11, 0xff16, 0xff2c,
+ 0xff25, 0xff0c, 0xff3f, 0xff23,
+ 0xff33, 0xff54, 0xff44, 0xff33,
+ 0xff54, 0xff52, 0xff49, 0xff4e,
+ 0xff47, 0xff11, 0xff16, 0x0 };
+#endif
+
+static const char refutf16LE3[] = "T\377E\377S\377T\377?\377S\377T\377"
+ "R\377I\377N\377G\377#\377H\377A\377"
+ "R\377S\377E\377T\377\064\377O\377\065"
+ "\377T\377F\377\030\377";
+
+#if 0
+static const uint16_t refutf16LE4[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff54, 0xff46,
+ 0xff11, 0xff16, 0xff2c, 0xff25,
+ 0xff54, 0xff4f, 0xff35, 0xff34,
+ 0xff26, 0xff18, 0x0 };
+
+static const uint32_t refutf32LE1[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff54, 0xff46,
+ 0xff18, 0xff34, 0xff4f, 0xff1a,
+ 0xff3f, 0xff43, 0xff48, 0xff41,
+ 0xff52, 0xff53, 0xff45, 0xff54,
+ 0xff3f, 0xff35, 0xff34, 0xff26,
+ 0xff0d, 0xff13, 0xff12, 0xff2c,
+ 0xff25, 0xff0c, 0xff3f, 0xff23,
+ 0xff33, 0xff54, 0xff44, 0xff33,
+ 0xff54, 0xff52, 0xff49, 0xff4e,
+ 0xff47, 0xff13, 0xff12, 0xff3f,
+#ifdef TARGET_DARWIN
+ 0x0 };
+#else
+ 0x1f42d, 0x1f42e, 0x0 };
+#endif
+
+static const uint16_t refutf16BE[] = { 0x54ff, 0x45ff, 0x53ff, 0x54ff,
+ 0x3fff, 0x55ff, 0x54ff, 0x46ff,
+ 0x11ff, 0x16ff, 0x22ff, 0x25ff,
+ 0x54ff, 0x4fff, 0x35ff, 0x34ff,
+ 0x26ff, 0x18ff, 0x0};
+
+static const uint16_t refucs2[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff43, 0xff53,
+ 0xff12, 0xff54, 0xff4f, 0xff35,
+ 0xff34, 0xff26, 0xff18, 0x0 };
+#endif
+
+class TestCharsetConverter : public testing::Test
+{
+protected:
+ TestCharsetConverter()
+ {
+ /* Add default settings for locale.
+ * Settings here are taken from CGUISettings::Initialize()
+ */
+ /*
+ //! @todo implement
+ CSettingsCategory *loc = CServiceBroker::GetSettingsComponent()->GetSettings()->AddCategory(7, "locale", 14090);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->AddString(loc, CSettings::SETTING_LOCALE_LANGUAGE,248,"english",
+ SPIN_CONTROL_TEXT);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->AddString(loc, CSettings::SETTING_LOCALE_COUNTRY, 20026, "USA",
+ SPIN_CONTROL_TEXT);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->AddString(loc, CSettings::SETTING_LOCALE_CHARSET, 14091, "DEFAULT",
+ SPIN_CONTROL_TEXT); // charset is set by the
+ // language file
+
+ // Add default settings for subtitles
+ CSettingsCategory *sub = CServiceBroker::GetSettingsComponent()->GetSettings()->AddCategory(5, "subtitles", 287);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->AddString(sub, CSettings::SETTING_SUBTITLES_CHARSET, 735, "DEFAULT",
+ SPIN_CONTROL_TEXT);
+ */
+ g_charsetConverter.reset();
+ g_charsetConverter.clear();
+ }
+
+ ~TestCharsetConverter() override
+ {
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Unload();
+ }
+
+ std::string refstra1, refstra2, varstra1;
+ std::wstring refstrw1, varstrw1;
+ std::string refstr1;
+};
+
+TEST_F(TestCharsetConverter, utf8ToW)
+{
+ refstra1 = "test utf8ToW";
+ refstrw1 = L"test utf8ToW";
+ varstrw1.clear();
+ g_charsetConverter.utf8ToW(refstra1, varstrw1, true, false, false);
+ EXPECT_STREQ(refstrw1.c_str(), varstrw1.c_str());
+}
+
+
+//TEST_F(TestCharsetConverter, utf16LEtoW)
+//{
+// refstrw1 = L"test_utf16LEtow";
+// //! @todo Should be able to use '=' operator instead of assign()
+// std::wstring refstr16_1;
+// refstr16_1.assign(refutf16LE1);
+// varstrw1.clear();
+// g_charsetConverter.utf16LEtoW(refstr16_1, varstrw1);
+// EXPECT_STREQ(refstrw1.c_str(), varstrw1.c_str());
+//}
+
+TEST_F(TestCharsetConverter, subtitleCharsetToUtf8)
+{
+ refstra1 = "test subtitleCharsetToW";
+ varstra1.clear();
+ g_charsetConverter.subtitleCharsetToUtf8(refstra1, varstra1);
+
+ /* Assign refstra1 to refstrw1 so that we can compare */
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8ToStringCharset_1)
+{
+ refstra1 = "test utf8ToStringCharset";
+ varstra1.clear();
+ g_charsetConverter.utf8ToStringCharset(refstra1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8ToStringCharset_2)
+{
+ refstra1 = "test utf8ToStringCharset";
+ varstra1 = "test utf8ToStringCharset";
+ g_charsetConverter.utf8ToStringCharset(varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8ToSystem)
+{
+ refstra1 = "test utf8ToSystem";
+ varstra1 = "test utf8ToSystem";
+ g_charsetConverter.utf8ToSystem(varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8To_ASCII)
+{
+ refstra1 = "test utf8To: charset ASCII, std::string";
+ varstra1.clear();
+ g_charsetConverter.utf8To("ASCII", refstra1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+/*
+TEST_F(TestCharsetConverter, utf8To_UTF16LE)
+{
+ refstra1 = "test_utf8To:_charset_UTF-16LE,_"
+ "CStdString16";
+ refstr16_1.assign(refutf16LE2);
+ varstr16_1.clear();
+ g_charsetConverter.utf8To("UTF-16LE", refstra1, varstr16_1);
+ EXPECT_TRUE(!memcmp(refstr16_1.c_str(), varstr16_1.c_str(),
+ refstr16_1.length() * sizeof(uint16_t)));
+}
+*/
+
+//TEST_F(TestCharsetConverter, utf8To_UTF32LE)
+//{
+// refstra1 = "test_utf8To:_charset_UTF-32LE,_"
+//#ifdef TARGET_DARWIN
+///* OSX has its own 'special' utf-8 charset which we use (see UTF8_SOURCE in CharsetConverter.cpp)
+// which is basically NFD (decomposed) utf-8. The trouble is, it fails on the COW FACE and MOUSE FACE
+// characters for some reason (possibly anything over 0x100000, or maybe there's a decomposed form of these
+// that I couldn't find???) If UTF8_SOURCE is switched to UTF-8 then this test would pass as-is, but then
+// some filenames stored in utf8-mac wouldn't display correctly in the UI. */
+// "CStdString32_";
+//#else
+// "CStdString32_🐭🐮";
+//#endif
+// refstr32_1.assign(refutf32LE1);
+// varstr32_1.clear();
+// g_charsetConverter.utf8To("UTF-32LE", refstra1, varstr32_1);
+// EXPECT_TRUE(!memcmp(refstr32_1.c_str(), varstr32_1.c_str(),
+// sizeof(refutf32LE1)));
+//}
+
+TEST_F(TestCharsetConverter, stringCharsetToUtf8)
+{
+ refstra1 = "test_stringCharsetToUtf8";
+ varstra1.clear();
+ g_charsetConverter.ToUtf8("UTF-16LE", refutf16LE3, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, isValidUtf8_1)
+{
+ varstra1.clear();
+ g_charsetConverter.ToUtf8("UTF-16LE", refutf16LE3, varstra1);
+ EXPECT_TRUE(CUtf8Utils::isValidUtf8(varstra1.c_str()));
+}
+
+TEST_F(TestCharsetConverter, isValidUtf8_2)
+{
+ refstr1 = refutf16LE3;
+ EXPECT_FALSE(CUtf8Utils::isValidUtf8(refstr1));
+}
+
+TEST_F(TestCharsetConverter, isValidUtf8_3)
+{
+ varstra1.clear();
+ g_charsetConverter.ToUtf8("UTF-16LE", refutf16LE3, varstra1);
+ EXPECT_TRUE(CUtf8Utils::isValidUtf8(varstra1.c_str()));
+}
+
+TEST_F(TestCharsetConverter, isValidUtf8_4)
+{
+ EXPECT_FALSE(CUtf8Utils::isValidUtf8(refutf16LE3));
+}
+
+//! @todo Resolve correct input/output for this function
+// TEST_F(TestCharsetConverter, ucs2CharsetToStringCharset)
+// {
+// void ucs2CharsetToStringCharset(const std::wstring& strSource,
+// std::string& strDest, bool swap = false);
+// }
+
+TEST_F(TestCharsetConverter, wToUTF8)
+{
+ refstrw1 = L"test_wToUTF8";
+ refstra1 = u8"test_wToUTF8";
+ varstra1.clear();
+ g_charsetConverter.wToUTF8(refstrw1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+//TEST_F(TestCharsetConverter, utf16BEtoUTF8)
+//{
+// refstr16_1.assign(refutf16BE);
+// refstra1 = "test_utf16BEtoUTF8";
+// varstra1.clear();
+// g_charsetConverter.utf16BEtoUTF8(refstr16_1, varstra1);
+// EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+//}
+
+//TEST_F(TestCharsetConverter, utf16LEtoUTF8)
+//{
+// refstr16_1.assign(refutf16LE4);
+// refstra1 = "test_utf16LEtoUTF8";
+// varstra1.clear();
+// g_charsetConverter.utf16LEtoUTF8(refstr16_1, varstra1);
+// EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+//}
+
+//TEST_F(TestCharsetConverter, ucs2ToUTF8)
+//{
+// refstr16_1.assign(refucs2);
+// refstra1 = "test_ucs2toUTF8";
+// varstra1.clear();
+// g_charsetConverter.ucs2ToUTF8(refstr16_1, varstra1);
+// EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+//}
+
+TEST_F(TestCharsetConverter, utf8logicalToVisualBiDi)
+{
+ refstra1 = "test_utf8logicalToVisualBiDi";
+ refstra2 = "test_utf8logicalToVisualBiDi";
+ varstra1.clear();
+ g_charsetConverter.utf8logicalToVisualBiDi(refstra1, varstra1);
+ EXPECT_STREQ(refstra2.c_str(), varstra1.c_str());
+}
+
+//! @todo Resolve correct input/output for this function
+// TEST_F(TestCharsetConverter, utf32ToStringCharset)
+// {
+// void utf32ToStringCharset(const unsigned long* strSource, std::string& strDest);
+// }
+
+TEST_F(TestCharsetConverter, getCharsetLabels)
+{
+ std::vector<std::string> reflabels;
+ reflabels.emplace_back("Western Europe (ISO)");
+ reflabels.emplace_back("Central Europe (ISO)");
+ reflabels.emplace_back("South Europe (ISO)");
+ reflabels.emplace_back("Baltic (ISO)");
+ reflabels.emplace_back("Cyrillic (ISO)");
+ reflabels.emplace_back("Arabic (ISO)");
+ reflabels.emplace_back("Greek (ISO)");
+ reflabels.emplace_back("Hebrew (ISO)");
+ reflabels.emplace_back("Turkish (ISO)");
+ reflabels.emplace_back("Central Europe (Windows)");
+ reflabels.emplace_back("Cyrillic (Windows)");
+ reflabels.emplace_back("Western Europe (Windows)");
+ reflabels.emplace_back("Greek (Windows)");
+ reflabels.emplace_back("Turkish (Windows)");
+ reflabels.emplace_back("Hebrew (Windows)");
+ reflabels.emplace_back("Arabic (Windows)");
+ reflabels.emplace_back("Baltic (Windows)");
+ reflabels.emplace_back("Vietnamese (Windows)");
+ reflabels.emplace_back("Thai (Windows)");
+ reflabels.emplace_back("Chinese Traditional (Big5)");
+ reflabels.emplace_back("Chinese Simplified (GBK)");
+ reflabels.emplace_back("Japanese (Shift-JIS)");
+ reflabels.emplace_back("Korean");
+ reflabels.emplace_back("Hong Kong (Big5-HKSCS)");
+
+ std::vector<std::string> varlabels = g_charsetConverter.getCharsetLabels();
+ ASSERT_EQ(reflabels.size(), varlabels.size());
+
+ size_t pos = 0;
+ for (const auto& it : varlabels)
+ {
+ EXPECT_STREQ((reflabels.at(pos++)).c_str(), it.c_str());
+ }
+}
+
+TEST_F(TestCharsetConverter, getCharsetLabelByName)
+{
+ std::string varstr =
+ g_charsetConverter.getCharsetLabelByName("ISO-8859-1");
+ EXPECT_STREQ("Western Europe (ISO)", varstr.c_str());
+ varstr.clear();
+ varstr = g_charsetConverter.getCharsetLabelByName("Bogus");
+ EXPECT_STREQ("", varstr.c_str());
+}
+
+TEST_F(TestCharsetConverter, getCharsetNameByLabel)
+{
+ std::string varstr =
+ g_charsetConverter.getCharsetNameByLabel("Western Europe (ISO)");
+ EXPECT_STREQ("ISO-8859-1", varstr.c_str());
+ varstr.clear();
+ varstr = g_charsetConverter.getCharsetNameByLabel("Bogus");
+ EXPECT_STREQ("", varstr.c_str());
+}
+
+TEST_F(TestCharsetConverter, unknownToUTF8_1)
+{
+ refstra1 = "test_unknownToUTF8";
+ varstra1 = "test_unknownToUTF8";
+ g_charsetConverter.unknownToUTF8(varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, unknownToUTF8_2)
+{
+ refstra1 = "test_unknownToUTF8";
+ varstra1.clear();
+ g_charsetConverter.unknownToUTF8(refstra1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, toW)
+{
+ refstra1 = "test_toW:_charset_UTF-16LE";
+ refstrw1 = L"\xBDEF\xEF94\x85BD\xBDEF\xEF93\x94BD\xBCEF\xEFBF"
+ L"\x94BD\xBDEF\xEF8F\xB7BC\xBCEF\xEF9A\xBFBC\xBDEF"
+ L"\xEF83\x88BD\xBDEF\xEF81\x92BD\xBDEF\xEF93\x85BD"
+ L"\xBDEF\xEF94\xBFBC\xBCEF\xEFB5\xB4BC\xBCEF\xEFA6"
+ L"\x8DBC\xBCEF\xEF91\x96BC\xBCEF\xEFAC\xA5BC";
+ varstrw1.clear();
+ g_charsetConverter.toW(refstra1, varstrw1, "UTF-16LE");
+ EXPECT_STREQ(refstrw1.c_str(), varstrw1.c_str());
+}
+
+TEST_F(TestCharsetConverter, fromW)
+{
+ refstrw1 = L"\xBDEF\xEF94\x85BD\xBDEF\xEF93\x94BD\xBCEF\xEFBF"
+ L"\x86BD\xBDEF\xEF92\x8FBD\xBDEF\xEF8D\xB7BC\xBCEF"
+ L"\xEF9A\xBFBC\xBDEF\xEF83\x88BD\xBDEF\xEF81\x92BD"
+ L"\xBDEF\xEF93\x85BD\xBDEF\xEF94\xBFBC\xBCEF\xEFB5"
+ L"\xB4BC\xBCEF\xEFA6\x8DBC\xBCEF\xEF91\x96BC\xBCEF"
+ L"\xEFAC\xA5BC";
+ refstra1 = "test_fromW:_charset_UTF-16LE";
+ varstra1.clear();
+ g_charsetConverter.fromW(refstrw1, varstra1, "UTF-16LE");
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
diff --git a/xbmc/utils/test/TestComponentContainer.cpp b/xbmc/utils/test/TestComponentContainer.cpp
new file mode 100644
index 0000000..d7246be
--- /dev/null
+++ b/xbmc/utils/test/TestComponentContainer.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/ComponentContainer.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+class BaseTestType
+{
+public:
+ virtual ~BaseTestType() = default;
+};
+
+struct DerivedType1 : public BaseTestType
+{
+ int a = 1;
+};
+
+struct DerivedType2 : public BaseTestType
+{
+ int a = 2;
+};
+
+struct DerivedType3 : public BaseTestType
+{
+ int a = 3;
+};
+
+class TestContainer : public CComponentContainer<BaseTestType>
+{
+ FRIEND_TEST(TestComponentContainer, Generic);
+};
+
+TEST(TestComponentContainer, Generic)
+{
+ TestContainer container;
+
+ // check that we can register types
+ container.RegisterComponent(std::make_shared<DerivedType1>());
+ EXPECT_EQ(container.size(), 1u);
+ container.RegisterComponent(std::make_shared<DerivedType2>());
+ EXPECT_EQ(container.size(), 2u);
+
+ // check that trying to register a component twice does nothing
+ container.RegisterComponent(std::make_shared<DerivedType2>());
+ EXPECT_EQ(container.size(), 2u);
+
+ // check that first component is valid
+ const auto t1 = container.GetComponent<DerivedType1>();
+ EXPECT_TRUE(t1 != nullptr);
+ EXPECT_EQ(t1->a, 1);
+
+ // check that second component is valid
+ const auto t2 = container.GetComponent<DerivedType2>();
+ EXPECT_TRUE(t2 != nullptr);
+ EXPECT_EQ(t2->a, 2);
+
+ // check that third component is not there
+ EXPECT_THROW(container.GetComponent<DerivedType3>(), std::logic_error);
+
+ // check that component instance is constant
+ const auto t4 = container.GetComponent<DerivedType1>();
+ EXPECT_EQ(t1.get(), t4.get());
+
+ // check we can call the const overload for GetComponent
+ // and that the returned type is const
+ const auto t5 = const_cast<const TestContainer&>(container).GetComponent<DerivedType1>();
+ EXPECT_TRUE(std::is_const_v<typename decltype(t5)::element_type>);
+}
diff --git a/xbmc/utils/test/TestCrc32.cpp b/xbmc/utils/test/TestCrc32.cpp
new file mode 100644
index 0000000..99a2dd5
--- /dev/null
+++ b/xbmc/utils/test/TestCrc32.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/Crc32.h"
+
+#include <gtest/gtest.h>
+
+static const char refdata[] = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "01234567890!@#$%^&*()";
+
+TEST(TestCrc32, Compute_1)
+{
+ Crc32 a;
+ uint32_t varcrc;
+ a.Compute(refdata, sizeof(refdata) - 1);
+ varcrc = a;
+ EXPECT_EQ(0xa4eb60e3, varcrc);
+}
+
+TEST(TestCrc32, Compute_2)
+{
+ uint32_t varcrc;
+ std::string s = refdata;
+ varcrc = Crc32::Compute(s);
+ EXPECT_EQ(0xa4eb60e3, varcrc);
+}
+
+TEST(TestCrc32, ComputeFromLowerCase)
+{
+ std::string s = refdata;
+ uint32_t varcrc = Crc32::ComputeFromLowerCase(s);
+ EXPECT_EQ((uint32_t)0x7f045b3e, varcrc);
+}
+
+TEST(TestCrc32, Reset)
+{
+ Crc32 a;
+ uint32_t varcrc;
+ std::string s = refdata;
+ a.Compute(s.c_str(), s.length());
+ a.Reset();
+ varcrc = a;
+ EXPECT_EQ(0xffffffff, varcrc);
+}
diff --git a/xbmc/utils/test/TestDatabaseUtils.cpp b/xbmc/utils/test/TestDatabaseUtils.cpp
new file mode 100644
index 0000000..ddb986c
--- /dev/null
+++ b/xbmc/utils/test/TestDatabaseUtils.cpp
@@ -0,0 +1,1377 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "dbwrappers/qry_dat.h"
+#include "music/MusicDatabase.h"
+#include "utils/DatabaseUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "video/VideoDatabase.h"
+
+#include <gtest/gtest.h>
+
+class TestDatabaseUtilsHelper
+{
+public:
+ TestDatabaseUtilsHelper()
+ {
+ album_idAlbum = CMusicDatabase::album_idAlbum;
+ album_strAlbum = CMusicDatabase::album_strAlbum;
+ album_strArtists = CMusicDatabase::album_strArtists;
+ album_strGenres = CMusicDatabase::album_strGenres;
+ album_strMoods = CMusicDatabase::album_strMoods;
+ album_strReleaseDate = CMusicDatabase::album_strReleaseDate;
+ album_strOrigReleaseDate = CMusicDatabase::album_strOrigReleaseDate;
+ album_strStyles = CMusicDatabase::album_strStyles;
+ album_strThemes = CMusicDatabase::album_strThemes;
+ album_strReview = CMusicDatabase::album_strReview;
+ album_strLabel = CMusicDatabase::album_strLabel;
+ album_strType = CMusicDatabase::album_strType;
+ album_fRating = CMusicDatabase::album_fRating;
+ album_iVotes = CMusicDatabase::album_iVotes;
+ album_iUserrating = CMusicDatabase::album_iUserrating;
+ album_dtDateAdded = CMusicDatabase::album_dateAdded;
+
+ song_idSong = CMusicDatabase::song_idSong;
+ song_strTitle = CMusicDatabase::song_strTitle;
+ song_iTrack = CMusicDatabase::song_iTrack;
+ song_iDuration = CMusicDatabase::song_iDuration;
+ song_strReleaseDate = CMusicDatabase::song_strReleaseDate;
+ song_strOrigReleaseDate = CMusicDatabase::song_strOrigReleaseDate;
+ song_strFileName = CMusicDatabase::song_strFileName;
+ song_iTimesPlayed = CMusicDatabase::song_iTimesPlayed;
+ song_iStartOffset = CMusicDatabase::song_iStartOffset;
+ song_iEndOffset = CMusicDatabase::song_iEndOffset;
+ song_lastplayed = CMusicDatabase::song_lastplayed;
+ song_rating = CMusicDatabase::song_rating;
+ song_votes = CMusicDatabase::song_votes;
+ song_userrating = CMusicDatabase::song_userrating;
+ song_comment = CMusicDatabase::song_comment;
+ song_strAlbum = CMusicDatabase::song_strAlbum;
+ song_strPath = CMusicDatabase::song_strPath;
+ song_strGenres = CMusicDatabase::song_strGenres;
+ song_strArtists = CMusicDatabase::song_strArtists;
+ }
+
+ int album_idAlbum;
+ int album_strAlbum;
+ int album_strArtists;
+ int album_strGenres;
+ int album_strMoods;
+ int album_strReleaseDate;
+ int album_strOrigReleaseDate;
+ int album_strStyles;
+ int album_strThemes;
+ int album_strReview;
+ int album_strLabel;
+ int album_strType;
+ int album_fRating;
+ int album_iVotes;
+ int album_iUserrating;
+ int album_dtDateAdded;
+
+ int song_idSong;
+ int song_strTitle;
+ int song_iTrack;
+ int song_iDuration;
+ int song_strReleaseDate;
+ int song_strOrigReleaseDate;
+ int song_strFileName;
+ int song_iTimesPlayed;
+ int song_iStartOffset;
+ int song_iEndOffset;
+ int song_lastplayed;
+ int song_rating;
+ int song_votes;
+ int song_userrating;
+ int song_comment;
+ int song_strAlbum;
+ int song_strPath;
+ int song_strGenres;
+ int song_strArtists;
+};
+
+TEST(TestDatabaseUtils, GetField_None)
+{
+ std::string refstr, varstr;
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldNone, MediaTypeNone,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = DatabaseUtils::GetField(FieldNone, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeAlbum)
+{
+ std::string refstr, varstr;
+
+ refstr = "albumview.idAlbum";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strAlbum";
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strArtists";
+ varstr = DatabaseUtils::GetField(FieldArtist, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strArtists";
+ varstr = DatabaseUtils::GetField(FieldAlbumArtist, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strGenres";
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strReleaseDate";
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+refstr = "albumview.strOrigReleaseDate";
+ varstr = DatabaseUtils::GetField(FieldOrigYear, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strMoods";
+ varstr = DatabaseUtils::GetField(FieldMoods, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strStyles";
+ varstr = DatabaseUtils::GetField(FieldStyles, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strThemes";
+ varstr = DatabaseUtils::GetField(FieldThemes, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strReview";
+ varstr = DatabaseUtils::GetField(FieldReview, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strLabel";
+ varstr = DatabaseUtils::GetField(FieldMusicLabel, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strType";
+ varstr = DatabaseUtils::GetField(FieldAlbumType, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.fRating";
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.iVotes";
+ varstr = DatabaseUtils::GetField(FieldVotes, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.iUserrating";
+ varstr = DatabaseUtils::GetField(FieldUserRating, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldNone, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strAlbum";
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeAlbum,
+ DatabaseQueryPartWhere);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeAlbum,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeSong)
+{
+ std::string refstr, varstr;
+
+ refstr = "songview.idSong";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strTitle";
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iTrack";
+ varstr = DatabaseUtils::GetField(FieldTrackNumber, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iDuration";
+ varstr = DatabaseUtils::GetField(FieldTime, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strFilename";
+ varstr = DatabaseUtils::GetField(FieldFilename, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iTimesPlayed";
+ varstr = DatabaseUtils::GetField(FieldPlaycount, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iStartOffset";
+ varstr = DatabaseUtils::GetField(FieldStartOffset, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iEndOffset";
+ varstr = DatabaseUtils::GetField(FieldEndOffset, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.lastPlayed";
+ varstr = DatabaseUtils::GetField(FieldLastPlayed, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.rating";
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.votes";
+ varstr = DatabaseUtils::GetField(FieldVotes, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.userrating";
+ varstr = DatabaseUtils::GetField(FieldUserRating, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.comment";
+ varstr = DatabaseUtils::GetField(FieldComment, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strReleaseDate";
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strOrigReleaseDate";
+ varstr = DatabaseUtils::GetField(FieldOrigYear, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strAlbum";
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strArtists";
+ varstr = DatabaseUtils::GetField(FieldArtist, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strArtists";
+ varstr = DatabaseUtils::GetField(FieldAlbumArtist, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strGenres";
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeSong,
+ DatabaseQueryPartWhere);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeSong,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeMusicVideo)
+{
+ std::string refstr, varstr;
+
+ refstr = "musicvideo_view.idMVideo";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideo_view.c{:02}", VIDEODB_ID_MUSICVIDEO_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideo_view.c{:02}", VIDEODB_ID_MUSICVIDEO_RUNTIME);
+ varstr = DatabaseUtils::GetField(FieldTime, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideo_view.c{:02}", VIDEODB_ID_MUSICVIDEO_DIRECTOR);
+ varstr = DatabaseUtils::GetField(FieldDirector, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideo_view.c{:02}", VIDEODB_ID_MUSICVIDEO_STUDIOS);
+ varstr = DatabaseUtils::GetField(FieldStudio, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideo_view.c{:02}", VIDEODB_ID_MUSICVIDEO_PLOT);
+ varstr = DatabaseUtils::GetField(FieldPlot, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideo_view.c{:02}", VIDEODB_ID_MUSICVIDEO_ALBUM);
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideo_view.c{:02}", VIDEODB_ID_MUSICVIDEO_ARTIST);
+ varstr = DatabaseUtils::GetField(FieldArtist, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideo_view.c{:02}", VIDEODB_ID_MUSICVIDEO_GENRE);
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideo_view.c{:02}", VIDEODB_ID_MUSICVIDEO_TRACK);
+ varstr = DatabaseUtils::GetField(FieldTrackNumber, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideo_view.strFilename";
+ varstr = DatabaseUtils::GetField(FieldFilename, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideo_view.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideo_view.playCount";
+ varstr = DatabaseUtils::GetField(FieldPlaycount, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideo_view.lastPlayed";
+ varstr = DatabaseUtils::GetField(FieldLastPlayed, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideo_view.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldVideoResolution, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideo_view.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeMusicVideo,
+ DatabaseQueryPartWhere);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideo_view.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeMusicVideo,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideo_view.userrating";
+ varstr = DatabaseUtils::GetField(FieldUserRating, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeMovie)
+{
+ std::string refstr, varstr;
+
+ refstr = "movie_view.idMovie";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("CASE WHEN length(movie_view.c{:02}) > 0 THEN movie_view.c{:02} "
+ "ELSE movie_view.c{:02} END",
+ VIDEODB_ID_SORTTITLE, VIDEODB_ID_SORTTITLE, VIDEODB_ID_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeMovie,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_PLOT);
+ varstr = DatabaseUtils::GetField(FieldPlot, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_PLOTOUTLINE);
+ varstr = DatabaseUtils::GetField(FieldPlotOutline, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_TAGLINE);
+ varstr = DatabaseUtils::GetField(FieldTagline, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movie_view.votes";
+ varstr = DatabaseUtils::GetField(FieldVotes, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movie_view.rating";
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_CREDITS);
+ varstr = DatabaseUtils::GetField(FieldWriter, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_SORTTITLE);
+ varstr = DatabaseUtils::GetField(FieldSortTitle, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_RUNTIME);
+ varstr = DatabaseUtils::GetField(FieldTime, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_MPAA);
+ varstr = DatabaseUtils::GetField(FieldMPAA, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_TOP250);
+ varstr = DatabaseUtils::GetField(FieldTop250, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_GENRE);
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_DIRECTOR);
+ varstr = DatabaseUtils::GetField(FieldDirector, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_STUDIOS);
+ varstr = DatabaseUtils::GetField(FieldStudio, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_TRAILER);
+ varstr = DatabaseUtils::GetField(FieldTrailer, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movie_view.c{:02}", VIDEODB_ID_COUNTRY);
+ varstr = DatabaseUtils::GetField(FieldCountry, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movie_view.strFilename";
+ varstr = DatabaseUtils::GetField(FieldFilename, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movie_view.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movie_view.playCount";
+ varstr = DatabaseUtils::GetField(FieldPlaycount, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movie_view.lastPlayed";
+ varstr = DatabaseUtils::GetField(FieldLastPlayed, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movie_view.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movie_view.userrating";
+ varstr = DatabaseUtils::GetField(FieldUserRating, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeTvShow)
+{
+ std::string refstr, varstr;
+
+ refstr = "tvshow_view.idShow";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr =
+ StringUtils::Format("CASE WHEN length(tvshow_view.c{:02}) > 0 THEN tvshow_view.c{:02} "
+ "ELSE tvshow_view.c{:02} END",
+ VIDEODB_ID_TV_SORTTITLE, VIDEODB_ID_TV_SORTTITLE, VIDEODB_ID_TV_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeTvShow,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshow_view.c{:02}", VIDEODB_ID_TV_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshow_view.c{:02}", VIDEODB_ID_TV_PLOT);
+ varstr = DatabaseUtils::GetField(FieldPlot, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshow_view.c{:02}", VIDEODB_ID_TV_STATUS);
+ varstr = DatabaseUtils::GetField(FieldTvShowStatus, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshow_view.votes";
+ varstr = DatabaseUtils::GetField(FieldVotes, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshow_view.rating";
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshow_view.c{:02}", VIDEODB_ID_TV_PREMIERED);
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshow_view.c{:02}", VIDEODB_ID_TV_GENRE);
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshow_view.c{:02}", VIDEODB_ID_TV_MPAA);
+ varstr = DatabaseUtils::GetField(FieldMPAA, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshow_view.c{:02}", VIDEODB_ID_TV_STUDIOS);
+ varstr = DatabaseUtils::GetField(FieldStudio, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshow_view.c{:02}", VIDEODB_ID_TV_SORTTITLE);
+ varstr = DatabaseUtils::GetField(FieldSortTitle, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshow_view.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshow_view.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshow_view.totalSeasons";
+ varstr = DatabaseUtils::GetField(FieldSeason, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshow_view.totalCount";
+ varstr = DatabaseUtils::GetField(FieldNumberOfEpisodes, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshow_view.watchedcount";
+ varstr = DatabaseUtils::GetField(FieldNumberOfWatchedEpisodes,
+ MediaTypeTvShow, DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshow_view.userrating";
+ varstr = DatabaseUtils::GetField(FieldUserRating, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeEpisode)
+{
+ std::string refstr, varstr;
+
+ refstr = "episode_view.idEpisode";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episode_view.c{:02}", VIDEODB_ID_EPISODE_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episode_view.c{:02}", VIDEODB_ID_EPISODE_PLOT);
+ varstr = DatabaseUtils::GetField(FieldPlot, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.votes";
+ varstr = DatabaseUtils::GetField(FieldVotes, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.rating";
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episode_view.c{:02}", VIDEODB_ID_EPISODE_CREDITS);
+ varstr = DatabaseUtils::GetField(FieldWriter, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episode_view.c{:02}", VIDEODB_ID_EPISODE_AIRED);
+ varstr = DatabaseUtils::GetField(FieldAirDate, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episode_view.c{:02}", VIDEODB_ID_EPISODE_RUNTIME);
+ varstr = DatabaseUtils::GetField(FieldTime, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episode_view.c{:02}", VIDEODB_ID_EPISODE_DIRECTOR);
+ varstr = DatabaseUtils::GetField(FieldDirector, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episode_view.c{:02}", VIDEODB_ID_EPISODE_SEASON);
+ varstr = DatabaseUtils::GetField(FieldSeason, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episode_view.c{:02}", VIDEODB_ID_EPISODE_EPISODE);
+ varstr = DatabaseUtils::GetField(FieldEpisodeNumber, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.strFilename";
+ varstr = DatabaseUtils::GetField(FieldFilename, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.playCount";
+ varstr = DatabaseUtils::GetField(FieldPlaycount, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.lastPlayed";
+ varstr = DatabaseUtils::GetField(FieldLastPlayed, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.strTitle";
+ varstr = DatabaseUtils::GetField(FieldTvShowTitle, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.premiered";
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.mpaa";
+ varstr = DatabaseUtils::GetField(FieldMPAA, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.strStudio";
+ varstr = DatabaseUtils::GetField(FieldStudio, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episode_view.userrating";
+ varstr = DatabaseUtils::GetField(FieldUserRating, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_FieldRandom)
+{
+ std::string refstr, varstr;
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeEpisode,
+ DatabaseQueryPartWhere);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "RANDOM()";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeEpisode,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_None)
+{
+ int refindex, varindex;
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeNone);
+ EXPECT_EQ(refindex, varindex);
+
+ varindex = DatabaseUtils::GetFieldIndex(FieldNone, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+}
+
+//! @todo Should enums in CMusicDatabase be made public instead?
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeAlbum)
+{
+ int refindex, varindex;
+ TestDatabaseUtilsHelper a;
+
+ refindex = a.album_idAlbum;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strAlbum;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbum, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strArtists;
+ varindex = DatabaseUtils::GetFieldIndex(FieldArtist, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strArtists;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbumArtist, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strGenres;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strReleaseDate;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strOrigReleaseDate;
+ varindex = DatabaseUtils::GetFieldIndex(FieldOrigYear, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strMoods;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMoods, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strStyles;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStyles, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strThemes;
+ varindex = DatabaseUtils::GetFieldIndex(FieldThemes, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strReview;
+ varindex = DatabaseUtils::GetFieldIndex(FieldReview, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strLabel;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMusicLabel, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strType;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbumType, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_fRating;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_dtDateAdded;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDateAdded, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeSong)
+{
+ int refindex, varindex;
+ TestDatabaseUtilsHelper a;
+
+ refindex = a.song_idSong;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strTitle;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iTrack;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTrackNumber, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iDuration;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTime, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strReleaseDate;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strFileName;
+ varindex = DatabaseUtils::GetFieldIndex(FieldFilename, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iTimesPlayed;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlaycount, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iStartOffset;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStartOffset, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iEndOffset;
+ varindex = DatabaseUtils::GetFieldIndex(FieldEndOffset, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_lastplayed;
+ varindex = DatabaseUtils::GetFieldIndex(FieldLastPlayed, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_rating;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_votes;
+ varindex = DatabaseUtils::GetFieldIndex(FieldVotes, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_userrating;
+ varindex = DatabaseUtils::GetFieldIndex(FieldUserRating, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_comment;
+ varindex = DatabaseUtils::GetFieldIndex(FieldComment, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strAlbum;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbum, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strPath;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strArtists;
+ varindex = DatabaseUtils::GetFieldIndex(FieldArtist, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strGenres;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeMusicVideo)
+{
+ int refindex, varindex;
+
+ refindex = 0;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_TITLE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_RUNTIME + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTime, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_DIRECTOR + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDirector, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_STUDIOS + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStudio, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_PLOT + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlot, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_ALBUM + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbum, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_ARTIST + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldArtist, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_GENRE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_TRACK + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTrackNumber, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_FILE;
+ varindex = DatabaseUtils::GetFieldIndex(FieldFilename, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_PATH;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlaycount, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldLastPlayed, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_DATEADDED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDateAdded, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_USER_RATING;
+ varindex = DatabaseUtils::GetFieldIndex(FieldUserRating, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_PREMIERED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeMovie)
+{
+ int refindex, varindex;
+
+ refindex = 0;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TITLE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_SORTTITLE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldSortTitle, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_PLOT + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlot, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_PLOTOUTLINE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlotOutline, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TAGLINE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTagline, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_CREDITS + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldWriter, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_RUNTIME + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTime, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MPAA + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMPAA, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TOP250 + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTop250, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_GENRE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_DIRECTOR + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDirector, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_STUDIOS + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStudio, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TRAILER + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTrailer, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_COUNTRY + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldCountry, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_FILE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldFilename, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_PATH;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_PLAYCOUNT;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlaycount, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_LASTPLAYED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldLastPlayed, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_DATEADDED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDateAdded, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_USER_RATING;
+ varindex = DatabaseUtils::GetFieldIndex(FieldUserRating, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_VOTES;
+ varindex = DatabaseUtils::GetFieldIndex(FieldVotes, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_RATING;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_PREMIERED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeTvShow)
+{
+ int refindex, varindex;
+
+ refindex = 0;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_TITLE + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_SORTTITLE + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldSortTitle, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_PLOT + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlot, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_STATUS + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTvShowStatus, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_PREMIERED + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_GENRE + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_MPAA + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMPAA, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_STUDIOS + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStudio, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_PATH;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_DATEADDED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDateAdded, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_NUM_EPISODES;
+ varindex = DatabaseUtils::GetFieldIndex(FieldNumberOfEpisodes, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_NUM_WATCHED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldNumberOfWatchedEpisodes, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_NUM_SEASONS;
+ varindex = DatabaseUtils::GetFieldIndex(FieldSeason, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_USER_RATING;
+ varindex = DatabaseUtils::GetFieldIndex(FieldUserRating, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_VOTES;
+ varindex = DatabaseUtils::GetFieldIndex(FieldVotes, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_RATING;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeEpisode)
+{
+ int refindex, varindex;
+
+ refindex = 0;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_TITLE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_PLOT + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlot, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_CREDITS + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldWriter, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_AIRED + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAirDate, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_RUNTIME + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTime, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_DIRECTOR + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDirector, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_SEASON + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldSeason, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_EPISODE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldEpisodeNumber, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_FILE;
+ varindex = DatabaseUtils::GetFieldIndex(FieldFilename, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_PATH;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_PLAYCOUNT;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlaycount, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_LASTPLAYED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldLastPlayed, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_DATEADDED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDateAdded, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_TVSHOW_NAME;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTvShowTitle, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStudio, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMPAA, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_USER_RATING;
+ varindex = DatabaseUtils::GetFieldIndex(FieldUserRating, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_VOTES;
+ varindex = DatabaseUtils::GetFieldIndex(FieldVotes, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_RATING;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetSelectFields)
+{
+ Fields fields;
+ FieldList fieldlist;
+
+ EXPECT_FALSE(DatabaseUtils::GetSelectFields(fields, MediaTypeAlbum,
+ fieldlist));
+
+ fields.insert(FieldId);
+ fields.insert(FieldGenre);
+ fields.insert(FieldAlbum);
+ fields.insert(FieldArtist);
+ fields.insert(FieldTitle);
+ EXPECT_FALSE(DatabaseUtils::GetSelectFields(fields, MediaTypeNone,
+ fieldlist));
+ EXPECT_TRUE(DatabaseUtils::GetSelectFields(fields, MediaTypeAlbum,
+ fieldlist));
+ EXPECT_FALSE(fieldlist.empty());
+}
+
+TEST(TestDatabaseUtils, GetFieldValue)
+{
+ CVariant v_null, v_string;
+ dbiplus::field_value f_null, f_string("test");
+
+ f_null.set_isNull();
+ EXPECT_TRUE(DatabaseUtils::GetFieldValue(f_null, v_null));
+ EXPECT_TRUE(v_null.isNull());
+
+ EXPECT_TRUE(DatabaseUtils::GetFieldValue(f_string, v_string));
+ EXPECT_FALSE(v_string.isNull());
+ EXPECT_TRUE(v_string.isString());
+}
+
+//! @todo Need some way to test this function
+// TEST(TestDatabaseUtils, GetDatabaseResults)
+// {
+// static bool GetDatabaseResults(MediaType mediaType, const FieldList &fields,
+// const std::unique_ptr<dbiplus::Dataset> &dataset,
+// DatabaseResults &results);
+// }
+
+TEST(TestDatabaseUtils, BuildLimitClause)
+{
+ std::string a = DatabaseUtils::BuildLimitClause(100);
+ EXPECT_STREQ(" LIMIT 100", a.c_str());
+}
+
+// class DatabaseUtils
+// {
+// public:
+//
+//
+// static std::string BuildLimitClause(int end, int start = 0);
+// };
diff --git a/xbmc/utils/test/TestDigest.cpp b/xbmc/utils/test/TestDigest.cpp
new file mode 100644
index 0000000..96d0529
--- /dev/null
+++ b/xbmc/utils/test/TestDigest.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/Digest.h"
+
+#include <gtest/gtest.h>
+
+using KODI::UTILITY::CDigest;
+using KODI::UTILITY::TypedDigest;
+
+TEST(TestDigest, Digest_Empty)
+{
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::MD5, "").c_str(), "d41d8cd98f00b204e9800998ecf8427e");
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::MD5, nullptr, 0).c_str(), "d41d8cd98f00b204e9800998ecf8427e");
+ {
+ CDigest digest{CDigest::Type::MD5};
+ EXPECT_STREQ(digest.Finalize().c_str(), "d41d8cd98f00b204e9800998ecf8427e");
+ }
+ {
+ CDigest digest{CDigest::Type::MD5};
+ digest.Update("");
+ digest.Update(nullptr, 0);
+ EXPECT_STREQ(digest.Finalize().c_str(), "d41d8cd98f00b204e9800998ecf8427e");
+ }
+}
+
+TEST(TestDigest, Digest_Basic)
+{
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::MD5, "asdf").c_str(), "912ec803b2ce49e4a541068d495ab570");
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::MD5, "asdf", 4).c_str(), "912ec803b2ce49e4a541068d495ab570");
+ {
+ CDigest digest{CDigest::Type::MD5};
+ digest.Update("as");
+ digest.Update("df", 2);
+ EXPECT_STREQ(digest.Finalize().c_str(), "912ec803b2ce49e4a541068d495ab570");
+ }
+}
+
+TEST(TestDigest, Digest_SHA1)
+{
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::SHA1, "").c_str(), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::SHA1, "asdf").c_str(), "3da541559918a808c2402bba5012f6c60b27661c");
+}
+
+TEST(TestDigest, Digest_SHA256)
+{
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::SHA256, "").c_str(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::SHA256, "asdf").c_str(), "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b");
+}
+
+TEST(TestDigest, Digest_SHA512)
+{
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::SHA512, "").c_str(), "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
+ EXPECT_STREQ(CDigest::Calculate(CDigest::Type::SHA512, "asdf").c_str(), "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429080fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1");
+}
+
+TEST(TestDigest, TypedDigest_Empty)
+{
+ TypedDigest t1, t2;
+ EXPECT_EQ(t1, t2);
+ EXPECT_EQ(t1.type, CDigest::Type::INVALID);
+ EXPECT_EQ(t1.value, "");
+ EXPECT_TRUE(t1.Empty());
+ t1.type = CDigest::Type::SHA1;
+ EXPECT_TRUE(t1.Empty());
+}
+
+TEST(TestDigest, TypedDigest_SameType)
+{
+ TypedDigest t1{CDigest::Type::SHA1, "da39a3ee5e6b4b0d3255bfef95601890afd80709"};
+ TypedDigest t2{CDigest::Type::SHA1, "da39a3ee5e6b4b0d3255bfef95601890afd80708"};
+ EXPECT_NE(t1, t2);
+ EXPECT_FALSE(t1.Empty());
+}
+
+TEST(TestDigest, TypedDigest_CompareCase)
+{
+ TypedDigest t1{CDigest::Type::SHA1, "da39a3ee5e6b4b0d3255bfef95601890afd80708"};
+ TypedDigest t2{CDigest::Type::SHA1, "da39A3EE5e6b4b0d3255bfef95601890afd80708"};
+ EXPECT_EQ(t1, t2);
+}
+
+TEST(TestDigest, TypedDigest_DifferingType)
+{
+ TypedDigest t1{CDigest::Type::SHA1, "da39a3ee5e6b4b0d3255bfef95601890afd80709"};
+ TypedDigest t2{CDigest::Type::SHA256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"};
+ // Silence "unused expression" warning
+ bool a;
+ EXPECT_THROW(a = (t1 == t2), std::logic_error);
+ // Silence "unused variable" warning
+ (void)a;
+ EXPECT_THROW(a = (t1 != t2), std::logic_error);
+ (void)a;
+}
diff --git a/xbmc/utils/test/TestEndianSwap.cpp b/xbmc/utils/test/TestEndianSwap.cpp
new file mode 100644
index 0000000..70d3cf0
--- /dev/null
+++ b/xbmc/utils/test/TestEndianSwap.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/EndianSwap.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestEndianSwap, Endian_Swap16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_Swap16(0xFF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_Swap32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_Swap32(0xFF00FF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_Swap64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_Swap64(UINT64_C(0xFF00FF00FF00FF00));
+ EXPECT_EQ(ref, var);
+}
+
+#ifndef WORDS_BIGENDIAN
+TEST(TestEndianSwap, Endian_SwapLE16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_SwapLE16(0x00FF);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapLE32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_SwapLE32(0x00FF00FF);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapLE64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_SwapLE64(UINT64_C(0x00FF00FF00FF00FF));
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_SwapBE16(0xFF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_SwapBE32(0xFF00FF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_SwapBE64(UINT64_C(0xFF00FF00FF00FF00));
+ EXPECT_EQ(ref, var);
+}
+#else
+TEST(TestEndianSwap, Endian_SwapLE16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_SwapLE16(0xFF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapLE32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_SwapLE32(0xFF00FF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapLE64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_SwapLE64(UINT64_C(0xFF00FF00FF00FF00));
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_SwapBE16(0x00FF);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_SwapBE32(0x00FF00FF);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_SwapBE64(UINT64_C(0x00FF00FF00FF00FF));
+ EXPECT_EQ(ref, var);
+}
+#endif
diff --git a/xbmc/utils/test/TestExecString.cpp b/xbmc/utils/test/TestExecString.cpp
new file mode 100644
index 0000000..4577b87
--- /dev/null
+++ b/xbmc/utils/test/TestExecString.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2022 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "FileItem.h"
+#include "utils/ExecString.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestExecString, ctor_1)
+{
+ {
+ const CExecString exec("ActivateWindow(Video, \"C:\\test\\foo\")");
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "activatewindow");
+ EXPECT_EQ(exec.GetParams().size(), 2U);
+ EXPECT_EQ(exec.GetParams()[0], "Video");
+ EXPECT_EQ(exec.GetParams()[1], "C:\\test\\foo");
+ EXPECT_EQ(exec.GetExecString(), "ActivateWindow(Video, \"C:\\test\\foo\")");
+ }
+ {
+ const CExecString exec("ActivateWindow(Video, \"C:\\test\\foo\\\")");
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "activatewindow");
+ EXPECT_EQ(exec.GetParams().size(), 2U);
+ EXPECT_EQ(exec.GetParams()[0], "Video");
+ EXPECT_EQ(exec.GetParams()[1], "C:\\test\\foo");
+ EXPECT_EQ(exec.GetExecString(), "ActivateWindow(Video, \"C:\\test\\foo\\\")");
+ }
+ {
+ const CExecString exec("ActivateWindow(Video, \"C:\\\\test\\\\foo\\\\\")");
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "activatewindow");
+ EXPECT_EQ(exec.GetParams().size(), 2U);
+ EXPECT_EQ(exec.GetParams()[0], "Video");
+ EXPECT_EQ(exec.GetParams()[1], "C:\\test\\foo\\");
+ EXPECT_EQ(exec.GetExecString(), "ActivateWindow(Video, \"C:\\\\test\\\\foo\\\\\")");
+ }
+ {
+ const CExecString exec("ActivateWindow(Video, \"C:\\\\\\\\test\\\\\\foo\\\\\")");
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "activatewindow");
+ EXPECT_EQ(exec.GetParams().size(), 2U);
+ EXPECT_EQ(exec.GetParams()[0], "Video");
+ EXPECT_EQ(exec.GetParams()[1], "C:\\\\test\\\\foo\\");
+ EXPECT_EQ(exec.GetExecString(), "ActivateWindow(Video, \"C:\\\\\\\\test\\\\\\foo\\\\\")");
+ }
+ {
+ const CExecString exec("SetProperty(Foo,\"\")");
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "setproperty");
+ EXPECT_EQ(exec.GetParams().size(), 2U);
+ EXPECT_EQ(exec.GetParams()[0], "Foo");
+ EXPECT_EQ(exec.GetParams()[1], "");
+ EXPECT_EQ(exec.GetExecString(), "SetProperty(Foo,\"\")");
+ }
+ {
+ const CExecString exec("SetProperty(foo,ba(\"ba black )\",sheep))");
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "setproperty");
+ EXPECT_EQ(exec.GetParams().size(), 2U);
+ EXPECT_EQ(exec.GetParams()[0], "foo");
+ EXPECT_EQ(exec.GetParams()[1], "ba(\"ba black )\",sheep)");
+ EXPECT_EQ(exec.GetExecString(), "SetProperty(foo,ba(\"ba black )\",sheep))");
+ }
+}
+
+TEST(TestExecString, ctor_2)
+{
+ {
+ const CExecString exec("ActivateWindow", {"Video", "C:\\test\\foo"});
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "activatewindow");
+ EXPECT_EQ(exec.GetParams().size(), 2U);
+ EXPECT_EQ(exec.GetParams()[0], "Video");
+ EXPECT_EQ(exec.GetParams()[1], "C:\\test\\foo");
+ EXPECT_EQ(exec.GetExecString(), "ActivateWindow(Video,C:\\test\\foo)");
+ }
+}
+
+TEST(TestExecString, ctor_3)
+{
+ {
+ const CFileItem item("C:\\test\\foo", true);
+ const CExecString exec(item, "Video");
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "activatewindow");
+ EXPECT_EQ(exec.GetParams().size(), 3U);
+ EXPECT_EQ(exec.GetParams()[0], "Video");
+ EXPECT_EQ(exec.GetParams()[1], "\"C:\\\\test\\\\foo\\\\\"");
+ EXPECT_EQ(exec.GetParams()[2], "return");
+ EXPECT_EQ(exec.GetExecString(), "ActivateWindow(Video,\"C:\\\\test\\\\foo\\\\\",return)");
+ }
+ {
+ const CFileItem item("C:\\test\\foo\\", true);
+ const CExecString exec(item, "Video");
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "activatewindow");
+ EXPECT_EQ(exec.GetParams().size(), 3U);
+ EXPECT_EQ(exec.GetParams()[0], "Video");
+ EXPECT_EQ(exec.GetParams()[1], "\"C:\\\\test\\\\foo\\\\\"");
+ EXPECT_EQ(exec.GetParams()[2], "return");
+ EXPECT_EQ(exec.GetExecString(), "ActivateWindow(Video,\"C:\\\\test\\\\foo\\\\\",return)");
+ }
+ {
+ const CFileItem item("C:\\test\\foo", false);
+ const CExecString exec(item, "Video");
+ EXPECT_EQ(exec.IsValid(), true);
+ EXPECT_EQ(exec.GetFunction(), "playmedia");
+ EXPECT_EQ(exec.GetParams().size(), 1U);
+ EXPECT_EQ(exec.GetParams()[0], "\"C:\\\\test\\\\foo\"");
+ EXPECT_EQ(exec.GetExecString(), "PlayMedia(\"C:\\\\test\\\\foo\")");
+ }
+}
diff --git a/xbmc/utils/test/TestFileOperationJob.cpp b/xbmc/utils/test/TestFileOperationJob.cpp
new file mode 100644
index 0000000..1df243e
--- /dev/null
+++ b/xbmc/utils/test/TestFileOperationJob.cpp
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "filesystem/Directory.h"
+#include "filesystem/File.h"
+#include "test/TestUtils.h"
+#include "utils/FileOperationJob.h"
+#include "utils/URIUtils.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestFileOperationJob, ActionCopy)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ std::string destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "copy");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionCopy, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionCopy, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+ EXPECT_TRUE(XFILE::CFile::Delete(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
+
+TEST(TestFileOperationJob, ActionMove)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ std::string destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "move");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+ ASSERT_TRUE(XFILE::CDirectory::Create(destpath));
+
+ job.SetFileOperation(CFileOperationJob::ActionMove, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionMove, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_FALSE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ EXPECT_TRUE(XFILE::CFile::Delete(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+
+ EXPECT_FALSE(XBMC_DELETETEMPFILE(tmpfile));
+}
+
+TEST(TestFileOperationJob, ActionDelete)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ std::string destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "delete");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionCopy, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionCopy, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionDelete, items, "");
+ EXPECT_EQ(CFileOperationJob::ActionDelete, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_FALSE(XFILE::CFile::Exists(tmpfilepath));
+
+ items.Clear();
+ CFileItemPtr item2(new CFileItem(destfile));
+ item2->SetPath(destfile);
+ item2->m_bIsFolder = false;
+ item2->Select(true);
+ items.Add(item2);
+
+ job.SetFileOperation(CFileOperationJob::ActionDelete, items, "");
+ EXPECT_EQ(CFileOperationJob::ActionDelete, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_FALSE(XFILE::CFile::Exists(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+
+ EXPECT_FALSE(XBMC_DELETETEMPFILE(tmpfile));
+}
+
+TEST(TestFileOperationJob, ActionReplace)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ std::string destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "replace");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionCopy, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionCopy, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionReplace, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionReplace, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+ EXPECT_TRUE(XFILE::CFile::Delete(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
+
+TEST(TestFileOperationJob, ActionCreateFolder)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath, destpath;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ std::string tmpfiledirectory =
+ CXBMCTestUtils::Instance().TempFileDirectory(tmpfile);
+
+ tmpfile->Close();
+
+ destpath = tmpfilepath;
+ destpath += ".createfolder";
+ ASSERT_FALSE(XFILE::CFile::Exists(destpath));
+
+ CFileItemPtr item(new CFileItem(destpath));
+ item->SetPath(destpath);
+ item->m_bIsFolder = true;
+ item->Select(true);
+ items.Add(item);
+
+ job.SetFileOperation(CFileOperationJob::ActionCreateFolder, items, tmpfiledirectory);
+ EXPECT_EQ(CFileOperationJob::ActionCreateFolder, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CDirectory::Exists(destpath));
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
+
+// This test will fail until ActionDeleteFolder has a proper implementation
+TEST(TestFileOperationJob, ActionDeleteFolder)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath, destpath;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ std::string tmpfiledirectory =
+ CXBMCTestUtils::Instance().TempFileDirectory(tmpfile);
+
+ tmpfile->Close();
+
+ destpath = tmpfilepath;
+ destpath += ".deletefolder";
+ ASSERT_FALSE(XFILE::CFile::Exists(destpath));
+
+ CFileItemPtr item(new CFileItem(destpath));
+ item->SetPath(destpath);
+ item->m_bIsFolder = true;
+ item->Select(true);
+ items.Add(item);
+
+ job.SetFileOperation(CFileOperationJob::ActionCreateFolder, items, tmpfiledirectory);
+ EXPECT_EQ(CFileOperationJob::ActionCreateFolder, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CDirectory::Exists(destpath));
+
+ job.SetFileOperation(CFileOperationJob::ActionDeleteFolder, items, tmpfiledirectory);
+ EXPECT_EQ(CFileOperationJob::ActionDeleteFolder, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_FALSE(XFILE::CDirectory::Exists(destpath));
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+}
+
+TEST(TestFileOperationJob, GetFunctions)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ std::string destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "getfunctions");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionCopy, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionCopy, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ std::cout << "GetAverageSpeed(): " << job.GetAverageSpeed() << std::endl;
+ std::cout << "GetCurrentOperation(): " << job.GetCurrentOperation() << std::endl;
+ std::cout << "GetCurrentFile(): " << job.GetCurrentFile() << std::endl;
+ EXPECT_FALSE(job.GetItems().IsEmpty());
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+ EXPECT_TRUE(XFILE::CFile::Delete(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
diff --git a/xbmc/utils/test/TestFileUtils.cpp b/xbmc/utils/test/TestFileUtils.cpp
new file mode 100644
index 0000000..bb9b8b6
--- /dev/null
+++ b/xbmc/utils/test/TestFileUtils.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "FileItem.h"
+#include "filesystem/File.h"
+#include "test/TestUtils.h"
+#include "utils/FileUtils.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestFileUtils, DeleteItem_CFileItemPtr)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath;
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ tmpfile->Close(); //Close tmpfile before we try to delete it
+ EXPECT_TRUE(CFileUtils::DeleteItem(item));
+ EXPECT_FALSE(XBMC_DELETETEMPFILE(tmpfile));
+}
+
+TEST(TestFileUtils, DeleteItemString)
+{
+ XFILE::CFile *tmpfile;
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfile->Close(); //Close tmpfile before we try to delete it
+ EXPECT_TRUE(CFileUtils::DeleteItem(XBMC_TEMPFILEPATH(tmpfile)));
+ EXPECT_FALSE(XBMC_DELETETEMPFILE(tmpfile));
+}
+
+/* Executing RenameFile() requires input from the user */
+// static bool RenameFile(const std::string &strFile);
diff --git a/xbmc/utils/test/TestGlobalsHandling.cpp b/xbmc/utils/test/TestGlobalsHandling.cpp
new file mode 100644
index 0000000..5b8d26a
--- /dev/null
+++ b/xbmc/utils/test/TestGlobalsHandling.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/test/TestGlobalsHandlingPattern1.h"
+
+#include <gtest/gtest.h>
+
+using namespace xbmcutil;
+using namespace test;
+
+bool TestGlobalPattern1::ctorCalled = false;
+bool TestGlobalPattern1::dtorCalled = false;
+
+TEST(TestGlobal, Pattern1)
+{
+ EXPECT_TRUE(TestGlobalPattern1::ctorCalled);
+ {
+ std::shared_ptr<TestGlobalPattern1> ptr = g_testGlobalPattern1Ref;
+ }
+}
diff --git a/xbmc/utils/test/TestGlobalsHandlingPattern1.h b/xbmc/utils/test/TestGlobalsHandlingPattern1.h
new file mode 100644
index 0000000..92088b8
--- /dev/null
+++ b/xbmc/utils/test/TestGlobalsHandlingPattern1.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "utils/GlobalsHandling.h"
+
+#include <iostream>
+
+namespace xbmcutil
+{
+ namespace test
+ {
+ class TestGlobalPattern1
+ {
+ public:
+ static bool ctorCalled;
+ static bool dtorCalled;
+
+ int somethingToAccess = 0;
+
+ TestGlobalPattern1() { ctorCalled = true; }
+ ~TestGlobalPattern1()
+ {
+ std::cout << "Clean shutdown of TestGlobalPattern1" << std::endl << std::flush;
+ dtorCalled = true;
+ }
+
+ void beHappy() { if (somethingToAccess) throw somethingToAccess; }
+ };
+ }
+}
+
+XBMC_GLOBAL_REF(xbmcutil::test::TestGlobalPattern1,g_testGlobalPattern1);
+#define g_testGlobalPattern1 XBMC_GLOBAL_USE(xbmcutil::test::TestGlobalPattern1)
diff --git a/xbmc/utils/test/TestHTMLUtil.cpp b/xbmc/utils/test/TestHTMLUtil.cpp
new file mode 100644
index 0000000..7d0e515
--- /dev/null
+++ b/xbmc/utils/test/TestHTMLUtil.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/HTMLUtil.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestHTMLUtil, RemoveTags)
+{
+ std::string str;
+ str = "<!DOCTYPE html>\n"
+ "<html>\n"
+ " <head class=\"someclass\">\n"
+ " <body>\n"
+ " <p>blah blah blah</p>\n"
+ " </body>\n"
+ " </head>\n"
+ "</html>\n";
+ HTML::CHTMLUtil::RemoveTags(str);
+ EXPECT_STREQ("\n\n \n \n blah blah blah\n \n \n\n",
+ str.c_str());
+}
+
+TEST(TestHTMLUtil, ConvertHTMLToW)
+{
+ std::wstring inw, refstrw, varstrw;
+ inw = L"&aring;&amp;&euro;";
+ refstrw = L"\u00e5&\u20ac";
+ HTML::CHTMLUtil::ConvertHTMLToW(inw, varstrw);
+ EXPECT_STREQ(refstrw.c_str(), varstrw.c_str());
+}
diff --git a/xbmc/utils/test/TestHttpHeader.cpp b/xbmc/utils/test/TestHttpHeader.cpp
new file mode 100644
index 0000000..1aeecc7
--- /dev/null
+++ b/xbmc/utils/test/TestHttpHeader.cpp
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/HttpHeader.h"
+
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+#define CHECK_CNT_TYPE_NAME "Content-Type"
+#define CHECK_CONTENT_TYPE_HTML "text/html"
+#define CHECK_CONTENT_TYPE_HTML_CHRS "text/html; charset=WINDOWS-1251"
+#define CHECK_CONTENT_TYPE_XML_CHRS "text/xml; charset=uTf-8"
+#define CHECK_CONTENT_TYPE_TEXT "text/plain"
+#define CHECK_DATE_NAME "Date"
+#define CHECK_DATE_VALUE1 "Thu, 09 Jan 2014 17:58:30 GMT"
+#define CHECK_DATE_VALUE2 "Thu, 09 Jan 2014 20:21:20 GMT"
+#define CHECK_DATE_VALUE3 "Thu, 09 Jan 2014 20:25:02 GMT"
+#define CHECK_PROT_LINE_200 "HTTP/1.1 200 OK"
+#define CHECK_PROT_LINE_301 "HTTP/1.1 301 Moved Permanently"
+
+#define CHECK_HEADER_SMPL CHECK_PROT_LINE_200 "\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n" \
+ "\r\n"
+
+#define CHECK_HEADER_L1 CHECK_PROT_LINE_200 "\r\n" \
+ "Server: nginx/1.4.4\r\n" \
+ CHECK_DATE_NAME ": " CHECK_DATE_VALUE1 "\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML_CHRS "\r\n" \
+ "Transfer-Encoding: chunked\r\n" \
+ "Connection: close\r\n" \
+ "Set-Cookie: PHPSESSID=90857d437518db8f0944ca012761048a; path=/; domain=example.com\r\n" \
+ "Expires: Thu, 19 Nov 1981 08:52:00 GMT\r\n" \
+ "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n" \
+ "Pragma: no-cache\r\n" \
+ "Set-Cookie: user_country=ot; expires=Thu, 09-Jan-2014 18:58:30 GMT; path=/; domain=.example.com\r\n" \
+ "\r\n"
+
+#define CHECK_HEADER_R CHECK_PROT_LINE_301 "\r\n" \
+ "Server: nginx/1.4.4\r\n" \
+ CHECK_DATE_NAME ": " CHECK_DATE_VALUE2 "\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n" \
+ "Content-Length: 150\r\n" \
+ "Connection: close\r\n" \
+ "Location: http://www.Example.Com\r\n" \
+ "\r\n"
+
+#define CHECK_HEADER_L2 CHECK_PROT_LINE_200 "\r\n" \
+ CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n" \
+ "Server: Apache/2.4.7 (Unix) mod_wsgi/3.4 Python/2.7.5 OpenSSL/1.0.1e\r\n" \
+ "Last-Modified: Thu, 09 Jan 2014 20:10:28 GMT\r\n" \
+ "ETag: \"9a97-4ef8f335ebd10\"\r\n" \
+ "Accept-Ranges: bytes\r\n" \
+ "Content-Length: 33355\r\n" \
+ "Vary: Accept-Encoding\r\n" \
+ "Cache-Control: max-age=3600\r\n" \
+ "Expires: Thu, 09 Jan 2014 21:25:02 GMT\r\n" \
+ "Connection: close\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_XML_CHRS "\r\n" \
+ "\r\n"
+
+// local helper function: replace substrings
+std::string strReplace(const std::string& str, const std::string& from, const std::string& to)
+{
+ std::string result;
+ size_t prevPos = 0;
+ size_t pos;
+ const size_t len = str.length();
+
+ do
+ {
+ pos = str.find(from, prevPos);
+ result.append(str, prevPos, pos - prevPos);
+ if (pos >= len)
+ break;
+ result.append(to);
+ prevPos = pos + from.length();
+ } while (true);
+
+ return result;
+}
+
+TEST(TestHttpHeader, General)
+{
+ /* check freshly created object */
+ CHttpHeader testHdr;
+ EXPECT_TRUE(testHdr.GetHeader().empty()) << "Newly created object is not empty";
+ EXPECT_TRUE(testHdr.GetProtoLine().empty()) << "Newly created object has non-empty protocol line";
+ EXPECT_TRUE(testHdr.GetMimeType().empty()) << "Newly created object has non-empty MIME-type";
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Newly created object has non-empty charset";
+ EXPECT_TRUE(testHdr.GetValue("foo").empty()) << "Newly created object has some parameter";
+ EXPECT_TRUE(testHdr.GetValues("bar").empty()) << "Newly created object has some parameters";
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Newly created object has \"parsing finished\" state";
+
+ /* check general functions in simple case */
+ testHdr.Parse(CHECK_HEADER_SMPL);
+ EXPECT_FALSE(testHdr.GetHeader().empty()) << "Parsed header is empty";
+ EXPECT_FALSE(testHdr.GetProtoLine().empty()) << "Parsed header has empty protocol line";
+ EXPECT_FALSE(testHdr.GetMimeType().empty()) << "Parsed header has empty MIME-type";
+ EXPECT_FALSE(testHdr.GetValue(CHECK_CNT_TYPE_NAME).empty()) << "Parsed header has empty \"" CHECK_CNT_TYPE_NAME "\" parameter";
+ EXPECT_FALSE(testHdr.GetValues(CHECK_CNT_TYPE_NAME).empty()) << "Parsed header has no \"" CHECK_CNT_TYPE_NAME "\" parameters";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Parsed header has \"parsing not finished\" state";
+
+ /* check clearing of object */
+ testHdr.Clear();
+ EXPECT_TRUE(testHdr.GetHeader().empty()) << "Cleared object is not empty";
+ EXPECT_TRUE(testHdr.GetProtoLine().empty()) << "Cleared object has non-empty protocol line";
+ EXPECT_TRUE(testHdr.GetMimeType().empty()) << "Cleared object has non-empty MIME-type";
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Cleared object has non-empty charset";
+ EXPECT_TRUE(testHdr.GetValue(CHECK_CNT_TYPE_NAME).empty()) << "Cleared object has some parameter";
+ EXPECT_TRUE(testHdr.GetValues(CHECK_CNT_TYPE_NAME).empty()) << "Cleared object has some parameters";
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Cleared object has \"parsing finished\" state";
+
+ /* check general functions after object clearing */
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_FALSE(testHdr.GetHeader().empty()) << "Parsed header is empty";
+ EXPECT_FALSE(testHdr.GetProtoLine().empty()) << "Parsed header has empty protocol line";
+ EXPECT_FALSE(testHdr.GetMimeType().empty()) << "Parsed header has empty MIME-type";
+ EXPECT_FALSE(testHdr.GetValue(CHECK_CNT_TYPE_NAME).empty()) << "Parsed header has empty \"" CHECK_CNT_TYPE_NAME "\" parameter";
+ EXPECT_FALSE(testHdr.GetValues(CHECK_CNT_TYPE_NAME).empty()) << "Parsed header has no \"" CHECK_CNT_TYPE_NAME "\" parameters";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Parsed header has \"parsing not finished\" state";
+}
+
+TEST(TestHttpHeader, Parse)
+{
+ CHttpHeader testHdr;
+
+ /* check parsing line-by-line */
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Not completed header has \"parsing finished\" state";
+ testHdr.Parse(CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n");
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Not completed header has \"parsing finished\" state";
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ(CHECK_PROT_LINE_200, testHdr.GetProtoLine().c_str()) << "Wrong protocol line";
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+
+ /* check autoclearing when new header is parsed */
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Not completed header has \"parsing finished\" state";
+ EXPECT_TRUE(testHdr.GetValues(CHECK_CNT_TYPE_NAME).empty()) << "Cleared header has some parameters";
+ testHdr.Clear();
+ EXPECT_TRUE(testHdr.GetHeader().empty()) << "Cleared object is not empty";
+
+ /* general check parsing */
+ testHdr.Parse(CHECK_HEADER_SMPL);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_SMPL, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_L1, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_STREQ("Thu, 09 Jan 2014 17:58:30 GMT", testHdr.GetValue("Date").c_str()); // case-sensitive match of value
+ testHdr.Parse(CHECK_HEADER_L2);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_L2, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_STREQ("Thu, 09 Jan 2014 20:10:28 GMT", testHdr.GetValue("Last-Modified").c_str()); // case-sensitive match of value
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_R, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("Location").c_str()); // case-sensitive match of value
+
+ /* check support for '\n' line endings */
+ testHdr.Parse(strReplace(CHECK_HEADER_SMPL, "\r\n", "\n"));
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_SMPL, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ testHdr.Parse(strReplace(CHECK_HEADER_L1, "\r\n", "\n"));
+ EXPECT_STRCASEEQ(CHECK_HEADER_L1, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ testHdr.Parse(strReplace(CHECK_HEADER_L2, "\r\n", "\n"));
+ EXPECT_STRCASEEQ(CHECK_HEADER_L2, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ testHdr.Parse(CHECK_PROT_LINE_200 "\n" CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n"); // mixed "\n" and "\r\n"
+ testHdr.Parse("\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n\r\n", testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+
+ /* check trimming of whitespaces for parameter name and value */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML " \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ":" CHECK_CONTENT_TYPE_HTML " \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ":\t" CHECK_CONTENT_TYPE_HTML " \t \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ":\t " CHECK_CONTENT_TYPE_HTML " \t \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME "\t:" CHECK_CONTENT_TYPE_HTML " \t \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME " \t : " CHECK_CONTENT_TYPE_HTML " \t \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+}
+
+TEST(TestHttpHeader, Parse_Multiline)
+{
+ CHttpHeader testHdr;
+
+ /* Check multiline parameter parsing line-by-line */
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse(CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n");
+ testHdr.Parse("X-Comment: This\r\n"); // between singleline parameters
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\r\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\r\n");
+ testHdr.Parse(CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_TEXT "\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse("X-Comment: This\r\n"); // first parameter
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\r\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\r\n");
+ testHdr.Parse(CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_TEXT "\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse(CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n");
+ testHdr.Parse("X-Comment: This\r\n"); // last parameter
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\r\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse("X-Comment: This\r\n"); // the only parameter
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\r\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse("X-Comment: This\n"); // the only parameter with mixed ending style
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ /* Check multiline parameter parsing as one line */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\nX-Comment: This\r\n is\r\n multi\r\n line\r\n value\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_TEXT "\r\n\r\n"); // between singleline parameters
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\r\n is\r\n multi\r\n line\r\n value\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_TEXT "\r\n\r\n"); // first parameter
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\nX-Comment: This\r\n is\r\n multi\r\n line\r\n value\r\n\r\n"); // last parameter
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\r\n is\r\n multi\r\n line\r\n value\r\n\r\n"); // the only parameter
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\n is\r\n multi\r\n line\n value\r\n\n"); // the only parameter with mixed ending style
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ /* Check multiline parameter parsing as mixed one/many lines */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\n is\r\n multi\r\n");
+ testHdr.Parse(" line\n value\r\n\n"); // the only parameter with mixed ending style
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ /* Check parsing of multiline parameter with ':' in value */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\r\n is:\r\n mul:ti\r\n");
+ testHdr.Parse(" :line\r\n valu:e\r\n\n"); // the only parameter
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is: mul:ti :line valu:e", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ /* Check multiline parameter parsing with trimming */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse(CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n");
+ testHdr.Parse("Server: Apache/2.4.7 (Unix)\r\n"); // last parameter, line-by-line parsing
+ testHdr.Parse(" mod_wsgi/3.4 \r\n");
+ testHdr.Parse("\tPython/2.7.5\r\n");
+ testHdr.Parse("\t \t \tOpenSSL/1.0.1e\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_GE(strlen("Apache/2.4.7 (Unix) mod_wsgi/3.4 \tPython/2.7.5\t \t \tOpenSSL/1.0.1e"), testHdr.GetValue("Server").length()) << "Length of miltiline value is greater than length of original string";
+ EXPECT_LE(strlen("Apache/2.4.7 (Unix) mod_wsgi/3.4 Python/2.7.5 OpenSSL/1.0.1e"), testHdr.GetValue("Server").length()) << "Length of miltiline value is less than length of trimmed original string";
+ EXPECT_STREQ("Apache/2.4.7(Unix)mod_wsgi/3.4Python/2.7.5OpenSSL/1.0.1e", strReplace(strReplace(testHdr.GetValue("Server"), " ", ""), "\t", "").c_str()) << "Multiline value with removed whitespaces does not match original string with removed whitespaces";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse(CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n");
+ testHdr.Parse("Server: Apache/2.4.7 (Unix)\r\n mod_wsgi/3.4 \n"); // last parameter, mixed line-by-line/one line parsing, mixed line ending
+ testHdr.Parse("\tPython/2.7.5\n\t \t \tOpenSSL/1.0.1e\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_GE(strlen("Apache/2.4.7 (Unix) mod_wsgi/3.4 \tPython/2.7.5\t \t \tOpenSSL/1.0.1e"), testHdr.GetValue("Server").length()) << "Length of miltiline value is greater than length of original string";
+ EXPECT_LE(strlen("Apache/2.4.7 (Unix) mod_wsgi/3.4 Python/2.7.5 OpenSSL/1.0.1e"), testHdr.GetValue("Server").length()) << "Length of miltiline value is less than length of trimmed original string";
+ EXPECT_STREQ("Apache/2.4.7(Unix)mod_wsgi/3.4Python/2.7.5OpenSSL/1.0.1e", strReplace(strReplace(testHdr.GetValue("Server"), " ", ""), "\t", "").c_str()) << "Multiline value with removed whitespaces does not match original string with removed whitespaces";
+}
+
+TEST(TestHttpHeader, GetValue)
+{
+ CHttpHeader testHdr;
+
+ /* Check that all parameters values can be retrieved */
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ(CHECK_DATE_VALUE2, testHdr.GetValue(CHECK_DATE_NAME).c_str()) << "Wrong parameter value";
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("150", testHdr.GetValue("Content-Length").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("close", testHdr.GetValue("Connection").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("Location").c_str()) << "Wrong parameter value";
+ EXPECT_TRUE(testHdr.GetValue("foo").empty()) << "Some value is returned for non-existed parameter";
+
+ /* Check that all parameters values can be retrieved in random order */
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("Location").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("Location").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("close", testHdr.GetValue("Connection").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("150", testHdr.GetValue("Content-Length").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ(CHECK_DATE_VALUE2, testHdr.GetValue(CHECK_DATE_NAME).c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ EXPECT_TRUE(testHdr.GetValue("foo").empty()) << "Some value is returned for non-existed parameter";
+
+ /* Check that parameters name is case-insensitive and value is case-sensitive*/
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("location").c_str()) << "Wrong parameter value for lowercase name";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("LOCATION").c_str()) << "Wrong parameter value for UPPERCASE name";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("LoCAtIOn").c_str()) << "Wrong parameter value for MiXEdcASe name";
+
+ /* Check value of last added parameter with the same name is returned */
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_STREQ("close", testHdr.GetValue("Connection").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("user_country=ot; expires=Thu, 09-Jan-2014 18:58:30 GMT; path=/; domain=.example.com", testHdr.GetValue("Set-Cookie").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("user_country=ot; expires=Thu, 09-Jan-2014 18:58:30 GMT; path=/; domain=.example.com", testHdr.GetValue("set-cookie").c_str()) << "Wrong parameter value for lowercase name";
+}
+
+TEST(TestHttpHeader, GetValues)
+{
+ CHttpHeader testHdr;
+
+ /* Check that all parameter values can be retrieved and order of values is correct */
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_EQ(1U, testHdr.GetValues("Server").size()) << "Wrong number of values for parameter \"Server\"";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValues("Server")[0].c_str()) << "Wrong parameter value";
+ EXPECT_EQ(2U, testHdr.GetValues("Set-Cookie").size()) << "Wrong number of values for parameter \"Set-Cookie\"";
+ EXPECT_STREQ("PHPSESSID=90857d437518db8f0944ca012761048a; path=/; domain=example.com", testHdr.GetValues("Set-Cookie")[0].c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("user_country=ot; expires=Thu, 09-Jan-2014 18:58:30 GMT; path=/; domain=.example.com", testHdr.GetValues("Set-Cookie")[1].c_str()) << "Wrong parameter value";
+ EXPECT_TRUE(testHdr.GetValues("foo").empty()) << "Some values are returned for non-existed parameter";
+}
+
+TEST(TestHttpHeader, AddParam)
+{
+ CHttpHeader testHdr;
+
+ /* General functionality */
+ testHdr.AddParam("server", "Microsoft-IIS/8.0");
+ EXPECT_STREQ("Microsoft-IIS/8.0", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+
+ /* Interfere with parsing */
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "\"AddParam\" set \"parsing finished\" state";
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nServer: nginx/1.4.4\r\n\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Parsed header has \"parsing not finished\" state";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ testHdr.AddParam("server", "Apache/2.4.7");
+ EXPECT_STREQ("Apache/2.4.7", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ EXPECT_EQ(3U, testHdr.GetValues("Server").size()) << "Wrong number of values for parameter \"Server\"";
+
+ /* Multiple values */
+ testHdr.AddParam("X-foo", "bar1");
+ testHdr.AddParam("x-foo", "bar2");
+ testHdr.AddParam("x-fOO", "bar3");
+ EXPECT_EQ(3U, testHdr.GetValues("X-FOO").size()) << "Wrong number of values for parameter \"X-foo\"";
+ EXPECT_STREQ("bar1", testHdr.GetValues("X-FOo")[0].c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("bar2", testHdr.GetValues("X-fOo")[1].c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("bar3", testHdr.GetValues("x-fOo")[2].c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("bar3", testHdr.GetValue("x-foo").c_str()) << "Wrong parameter value";
+
+ /* Overwrite value */
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Parsed header has \"parsing not finished\" state";
+ testHdr.AddParam("x-fOO", "superbar", true);
+ EXPECT_EQ(1U, testHdr.GetValues("X-FoO").size()) << "Wrong number of values for parameter \"X-foo\"";
+ EXPECT_STREQ("superbar", testHdr.GetValue("x-foo").c_str()) << "Wrong parameter value";
+
+ /* Check name trimming */
+ testHdr.AddParam("\tx-fOO\t ", "bar");
+ EXPECT_EQ(2U, testHdr.GetValues("X-FoO").size()) << "Wrong number of values for parameter \"X-foo\"";
+ EXPECT_STREQ("bar", testHdr.GetValue("x-foo").c_str()) << "Wrong parameter value";
+ testHdr.AddParam(" SerVer \t ", "fakeSrv", true);
+ EXPECT_EQ(1U, testHdr.GetValues("serveR").size()) << "Wrong number of values for parameter \"Server\"";
+ EXPECT_STREQ("fakeSrv", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+
+ /* Check value trimming */
+ testHdr.AddParam("X-TestParam", " testValue1");
+ EXPECT_STREQ("testValue1", testHdr.GetValue("X-TestParam").c_str()) << "Wrong parameter value";
+ testHdr.AddParam("X-TestParam", "\ttestValue2 and more \t ");
+ EXPECT_STREQ("testValue2 and more", testHdr.GetValue("X-TestParam").c_str()) << "Wrong parameter value";
+
+ /* Empty name or value */
+ testHdr.Clear();
+ testHdr.AddParam("X-TestParam", " ");
+ EXPECT_TRUE(testHdr.GetHeader().empty()) << "Parameter with empty value was added";
+ testHdr.AddParam("\t\t", "value");
+ EXPECT_TRUE(testHdr.GetHeader().empty());
+ testHdr.AddParam(" ", "\t");
+ EXPECT_TRUE(testHdr.GetHeader().empty());
+}
+
+TEST(TestHttpHeader, GetMimeType)
+{
+ CHttpHeader testHdr;
+
+ /* General functionality */
+ EXPECT_TRUE(testHdr.GetMimeType().empty()) << "Newly created object has non-empty MIME-type";
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nServer: nginx/1.4.4\r\n\r\n");
+ EXPECT_TRUE(testHdr.GetMimeType().empty()) << "Non-empty MIME-type for header without MIME-type";
+ testHdr.Parse(CHECK_HEADER_SMPL);
+ EXPECT_STREQ("text/html", testHdr.GetMimeType().c_str()) << "Wrong MIME-type";
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_STREQ("text/html", testHdr.GetMimeType().c_str()) << "Wrong MIME-type";
+ testHdr.Parse(CHECK_HEADER_L2);
+ EXPECT_STREQ("text/xml", testHdr.GetMimeType().c_str()) << "Wrong MIME-type";
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_STREQ("text/html", testHdr.GetMimeType().c_str()) << "Wrong MIME-type";
+
+ /* Overwrite by AddParam */
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, CHECK_CONTENT_TYPE_TEXT);
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_TEXT, testHdr.GetMimeType().c_str()) << "MIME-type was not overwritten by \"AddParam\"";
+
+ /* Correct trimming */
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, " " CHECK_CONTENT_TYPE_TEXT " \t ;foo=bar");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_TEXT, testHdr.GetMimeType().c_str()) << "MIME-type is not trimmed correctly";
+}
+
+
+TEST(TestHttpHeader, GetCharset)
+{
+ CHttpHeader testHdr;
+
+ /* General functionality */
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Newly created object has non-empty charset";
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nServer: nginx/1.4.4\r\n\r\n");
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Non-empty charset for header without charset";
+ testHdr.Parse(CHECK_HEADER_SMPL);
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Non-empty charset for header without charset";
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_STREQ("WINDOWS-1251", testHdr.GetCharset().c_str()) << "Wrong charset value";
+ testHdr.Parse(CHECK_HEADER_L2);
+ EXPECT_STREQ("UTF-8", testHdr.GetCharset().c_str()) << "Wrong charset value";
+
+ /* Overwrite by AddParam */
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, CHECK_CONTENT_TYPE_TEXT "; charset=WINDOWS-1252");
+ EXPECT_STREQ("WINDOWS-1252", testHdr.GetCharset().c_str()) << "Charset was not overwritten by \"AddParam\"";
+
+ /* Correct trimming */
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, "text/plain;charset=WINDOWS-1251");
+ EXPECT_STREQ("WINDOWS-1251", testHdr.GetCharset().c_str()) << "Wrong charset value";
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, "text/plain ;\tcharset=US-AScII\t");
+ EXPECT_STREQ("US-ASCII", testHdr.GetCharset().c_str()) << "Wrong charset value";
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, "text/html ; \tcharset=\"uTF-8\"\t");
+ EXPECT_STREQ("UTF-8", testHdr.GetCharset().c_str()) << "Wrong charset value";
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, " \ttext/xml\t;\tcharset=uTF-16 ");
+ EXPECT_STREQ("UTF-16", testHdr.GetCharset().c_str()) << "Wrong charset value";
+}
diff --git a/xbmc/utils/test/TestHttpParser.cpp b/xbmc/utils/test/TestHttpParser.cpp
new file mode 100644
index 0000000..1eb2932
--- /dev/null
+++ b/xbmc/utils/test/TestHttpParser.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/HttpParser.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestHttpParser, General)
+{
+ HttpParser a;
+ std::string str = "POST /path/script.cgi HTTP/1.0\r\n"
+ "From: amejia@xbmc.org\r\n"
+ "User-Agent: XBMC/snapshot (compatible; MSIE 5.5; Windows NT"
+ " 4.0)\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Content-Length: 35\r\n"
+ "\r\n"
+ "home=amejia&favorite+flavor=orange\r\n";
+ std::string refstr, varstr;
+
+ EXPECT_EQ(a.Done, a.addBytes(str.c_str(), str.length()));
+
+ refstr = "POST";
+ varstr = a.getMethod();
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "/path/script.cgi";
+ varstr = a.getUri();
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = a.getQueryString();
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "home=amejia&favorite+flavor=orange\r\n";
+ varstr = a.getBody();
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "application/x-www-form-urlencoded";
+ varstr = a.getValue("content-type");
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ EXPECT_EQ((unsigned)35, a.getContentLength());
+}
diff --git a/xbmc/utils/test/TestHttpRangeUtils.cpp b/xbmc/utils/test/TestHttpRangeUtils.cpp
new file mode 100644
index 0000000..f988f10
--- /dev/null
+++ b/xbmc/utils/test/TestHttpRangeUtils.cpp
@@ -0,0 +1,887 @@
+/*
+ * Copyright (C) 2015-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/HttpRangeUtils.h"
+
+#include <gtest/gtest.h>
+
+#define RANGES_START "bytes="
+
+static const uint64_t DefaultFirstPosition = 1;
+static const uint64_t DefaultLastPosition = 0;
+static const uint64_t DefaultLength = 0;
+static const void* DefaultData = NULL;
+
+TEST(TestHttpRange, FirstPosition)
+{
+ const uint64_t expectedFirstPosition = 25;
+
+ CHttpRange range;
+ EXPECT_EQ(DefaultFirstPosition, range.GetFirstPosition());
+
+ range.SetFirstPosition(expectedFirstPosition);
+ EXPECT_EQ(expectedFirstPosition, range.GetFirstPosition());
+}
+
+TEST(TestHttpRange, LastPosition)
+{
+ const uint64_t expectedLastPosition = 25;
+
+ CHttpRange range;
+ EXPECT_EQ(DefaultLastPosition, range.GetLastPosition());
+
+ range.SetLastPosition(expectedLastPosition);
+ EXPECT_EQ(expectedLastPosition, range.GetLastPosition());
+}
+
+TEST(TestHttpRange, Length)
+{
+ const uint64_t expectedFirstPosition = 10;
+ const uint64_t expectedLastPosition = 25;
+ const uint64_t expectedLength = expectedLastPosition - expectedFirstPosition + 1;
+
+ CHttpRange range;
+ EXPECT_EQ(DefaultLength, range.GetLength());
+
+ range.SetFirstPosition(expectedFirstPosition);
+ range.SetLastPosition(expectedLastPosition);
+ EXPECT_EQ(expectedLength, range.GetLength());
+
+ CHttpRange range_length;
+ range.SetFirstPosition(expectedFirstPosition);
+ range.SetLength(expectedLength);
+ EXPECT_EQ(expectedLastPosition, range.GetLastPosition());
+ EXPECT_EQ(expectedLength, range.GetLength());
+}
+
+TEST(TestHttpRange, IsValid)
+{
+ const uint64_t validFirstPosition = 10;
+ const uint64_t validLastPosition = 25;
+ const uint64_t invalidLastPosition = 5;
+
+ CHttpRange range;
+ EXPECT_FALSE(range.IsValid());
+
+ range.SetFirstPosition(validFirstPosition);
+ EXPECT_FALSE(range.IsValid());
+
+ range.SetLastPosition(invalidLastPosition);
+ EXPECT_FALSE(range.IsValid());
+
+ range.SetLastPosition(validLastPosition);
+ EXPECT_TRUE(range.IsValid());
+}
+
+TEST(TestHttpRange, Ctor)
+{
+ const uint64_t validFirstPosition = 10;
+ const uint64_t validLastPosition = 25;
+ const uint64_t invalidLastPosition = 5;
+ const uint64_t validLength = validLastPosition - validFirstPosition + 1;
+
+ CHttpRange range_invalid(validFirstPosition, invalidLastPosition);
+ EXPECT_EQ(validFirstPosition, range_invalid.GetFirstPosition());
+ EXPECT_EQ(invalidLastPosition, range_invalid.GetLastPosition());
+ EXPECT_EQ(DefaultLength, range_invalid.GetLength());
+ EXPECT_FALSE(range_invalid.IsValid());
+
+ CHttpRange range_valid(validFirstPosition, validLastPosition);
+ EXPECT_EQ(validFirstPosition, range_valid.GetFirstPosition());
+ EXPECT_EQ(validLastPosition, range_valid.GetLastPosition());
+ EXPECT_EQ(validLength, range_valid.GetLength());
+ EXPECT_TRUE(range_valid.IsValid());
+}
+
+TEST(TestHttpResponseRange, SetData)
+{
+ const uint64_t validFirstPosition = 1;
+ const uint64_t validLastPosition = 2;
+ const uint64_t validLength = validLastPosition - validFirstPosition + 1;
+ const char* validData = "test";
+ const void* invalidData = DefaultData;
+ const size_t validDataLength = strlen(validData);
+ const size_t invalidDataLength = 1;
+
+ CHttpResponseRange range;
+ EXPECT_EQ(DefaultData, range.GetData());
+ EXPECT_FALSE(range.IsValid());
+
+ range.SetData(invalidData);
+ EXPECT_EQ(invalidData, range.GetData());
+ EXPECT_FALSE(range.IsValid());
+
+ range.SetData(validData);
+ EXPECT_EQ(validData, range.GetData());
+ EXPECT_FALSE(range.IsValid());
+
+ range.SetData(invalidData, 0);
+ EXPECT_EQ(validData, range.GetData());
+ EXPECT_FALSE(range.IsValid());
+
+ range.SetData(invalidData, invalidDataLength);
+ EXPECT_EQ(invalidData, range.GetData());
+ EXPECT_FALSE(range.IsValid());
+
+ range.SetData(validData, validDataLength);
+ EXPECT_EQ(validData, range.GetData());
+ EXPECT_EQ(0U, range.GetFirstPosition());
+ EXPECT_EQ(validDataLength - 1, range.GetLastPosition());
+ EXPECT_EQ(validDataLength, range.GetLength());
+ EXPECT_TRUE(range.IsValid());
+
+ range.SetData(invalidData, 0, 0);
+ EXPECT_EQ(invalidData, range.GetData());
+ EXPECT_FALSE(range.IsValid());
+
+ range.SetData(validData, validFirstPosition, validLastPosition);
+ EXPECT_EQ(validData, range.GetData());
+ EXPECT_EQ(validFirstPosition, range.GetFirstPosition());
+ EXPECT_EQ(validLastPosition, range.GetLastPosition());
+ EXPECT_EQ(validLength, range.GetLength());
+ EXPECT_TRUE(range.IsValid());
+}
+
+TEST(TestHttpRanges, Ctor)
+{
+ CHttpRange range;
+ uint64_t position;
+
+ CHttpRanges ranges_empty;
+
+ EXPECT_EQ(0U, ranges_empty.Size());
+ EXPECT_TRUE(ranges_empty.Get().empty());
+
+ EXPECT_FALSE(ranges_empty.Get(0, range));
+ EXPECT_FALSE(ranges_empty.GetFirst(range));
+ EXPECT_FALSE(ranges_empty.GetLast(range));
+
+ EXPECT_FALSE(ranges_empty.GetFirstPosition(position));
+ EXPECT_FALSE(ranges_empty.GetLastPosition(position));
+ EXPECT_EQ(0U, ranges_empty.GetLength());
+ EXPECT_FALSE(ranges_empty.GetTotalRange(range));
+}
+
+TEST(TestHttpRanges, GetAll)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ const HttpRanges& ranges_raw_get = ranges.Get();
+ ASSERT_EQ(ranges_raw.size(), ranges_raw_get.size());
+
+ for (size_t i = 0; i < ranges_raw.size(); ++i)
+ EXPECT_EQ(ranges_raw.at(i), ranges_raw_get.at(i));
+}
+
+TEST(TestHttpRanges, GetIndex)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ CHttpRange range;
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range_0, range);
+
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range_1, range);
+
+ EXPECT_TRUE(ranges.Get(2, range));
+ EXPECT_EQ(range_2, range);
+
+ EXPECT_FALSE(ranges.Get(3, range));
+}
+
+TEST(TestHttpRanges, GetFirst)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ CHttpRange range;
+ EXPECT_TRUE(ranges.GetFirst(range));
+ EXPECT_EQ(range_0, range);
+}
+
+TEST(TestHttpRanges, GetLast)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ CHttpRange range;
+ EXPECT_TRUE(ranges.GetLast(range));
+ EXPECT_EQ(range_2, range);
+}
+
+TEST(TestHttpRanges, Size)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges_empty;
+ EXPECT_EQ(0U, ranges_empty.Size());
+
+ CHttpRanges ranges(ranges_raw);
+ EXPECT_EQ(ranges_raw.size(), ranges.Size());
+}
+
+TEST(TestHttpRanges, GetFirstPosition)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ uint64_t position;
+ EXPECT_TRUE(ranges.GetFirstPosition(position));
+ EXPECT_EQ(range_0.GetFirstPosition(), position);
+}
+
+TEST(TestHttpRanges, GetLastPosition)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ uint64_t position;
+ EXPECT_TRUE(ranges.GetLastPosition(position));
+ EXPECT_EQ(range_2.GetLastPosition(), position);
+}
+
+TEST(TestHttpRanges, GetLength)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+ const uint64_t expectedLength = range_0.GetLength() + range_1.GetLength() + range_2.GetLength();
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ EXPECT_EQ(expectedLength, ranges.GetLength());
+}
+
+TEST(TestHttpRanges, GetTotalRange)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+ CHttpRange range_total_expected(range_0.GetFirstPosition(), range_2.GetLastPosition());
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ CHttpRange range_total;
+ EXPECT_TRUE(ranges.GetTotalRange(range_total));
+ EXPECT_EQ(range_total_expected, range_total);
+}
+
+TEST(TestHttpRanges, Add)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ CHttpRanges ranges;
+ CHttpRange range;
+
+ ranges.Add(range_0);
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.GetFirst(range));
+ EXPECT_EQ(range_0, range);
+ EXPECT_TRUE(ranges.GetLast(range));
+ EXPECT_EQ(range_0, range);
+
+ ranges.Add(range_1);
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.GetFirst(range));
+ EXPECT_EQ(range_0, range);
+ EXPECT_TRUE(ranges.GetLast(range));
+ EXPECT_EQ(range_1, range);
+
+ ranges.Add(range_2);
+ EXPECT_EQ(3U, ranges.Size());
+ EXPECT_TRUE(ranges.GetFirst(range));
+ EXPECT_EQ(range_0, range);
+ EXPECT_TRUE(ranges.GetLast(range));
+ EXPECT_EQ(range_2, range);
+}
+
+TEST(TestHttpRanges, Remove)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ CHttpRange range;
+ EXPECT_EQ(3U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range_1, range);
+ EXPECT_TRUE(ranges.Get(2, range));
+ EXPECT_EQ(range_2, range);
+
+ // remove non-existing range
+ ranges.Remove(ranges.Size());
+ EXPECT_EQ(3U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range_1, range);
+ EXPECT_TRUE(ranges.Get(2, range));
+ EXPECT_EQ(range_2, range);
+
+ // remove first range
+ ranges.Remove(0);
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range_1, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range_2, range);
+
+ // remove last range
+ ranges.Remove(1);
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range_1, range);
+
+ // remove remaining range
+ ranges.Remove(0);
+ EXPECT_EQ(0U, ranges.Size());
+}
+
+TEST(TestHttpRanges, Clear)
+{
+ CHttpRange range_0(0, 2);
+ CHttpRange range_1(4, 6);
+ CHttpRange range_2(8, 10);
+
+ HttpRanges ranges_raw;
+ ranges_raw.push_back(range_0);
+ ranges_raw.push_back(range_1);
+ ranges_raw.push_back(range_2);
+
+ CHttpRanges ranges(ranges_raw);
+
+ CHttpRange range;
+ EXPECT_EQ(3U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range_1, range);
+ EXPECT_TRUE(ranges.Get(2, range));
+ EXPECT_EQ(range_2, range);
+
+ ranges.Clear();
+ EXPECT_EQ(0U, ranges.Size());
+}
+
+TEST(TestHttpRanges, ParseInvalid)
+{
+ CHttpRanges ranges;
+
+ // combinations of invalid string and invalid total length
+ EXPECT_FALSE(ranges.Parse(""));
+ EXPECT_FALSE(ranges.Parse("", 0));
+ EXPECT_FALSE(ranges.Parse("", 1));
+ EXPECT_FALSE(ranges.Parse("test", 0));
+ EXPECT_FALSE(ranges.Parse(RANGES_START, 0));
+
+ // empty range definition
+ EXPECT_FALSE(ranges.Parse(RANGES_START));
+ EXPECT_FALSE(ranges.Parse(RANGES_START "-"));
+
+ // bad characters in range definition
+ EXPECT_FALSE(ranges.Parse(RANGES_START "a"));
+ EXPECT_FALSE(ranges.Parse(RANGES_START "1a"));
+ EXPECT_FALSE(ranges.Parse(RANGES_START "1-a"));
+ EXPECT_FALSE(ranges.Parse(RANGES_START "a-a"));
+ EXPECT_FALSE(ranges.Parse(RANGES_START "a-1"));
+ EXPECT_FALSE(ranges.Parse(RANGES_START "--"));
+ EXPECT_FALSE(ranges.Parse(RANGES_START "1--"));
+ EXPECT_FALSE(ranges.Parse(RANGES_START "1--2"));
+ EXPECT_FALSE(ranges.Parse(RANGES_START "--2"));
+
+ // combination of valid and empty range definitions
+ EXPECT_FALSE(ranges.Parse(RANGES_START "0-1,"));
+ EXPECT_FALSE(ranges.Parse(RANGES_START ",0-1"));
+
+ // too big start position
+ EXPECT_FALSE(ranges.Parse(RANGES_START "10-11", 5));
+
+ // end position smaller than start position
+ EXPECT_FALSE(ranges.Parse(RANGES_START "1-0"));
+}
+
+TEST(TestHttpRanges, ParseStartOnly)
+{
+ const uint64_t totalLength = 5;
+ const CHttpRange range0_(0, totalLength - 1);
+ const CHttpRange range2_(2, totalLength - 1);
+
+ CHttpRange range;
+
+ CHttpRanges ranges_all;
+ EXPECT_TRUE(ranges_all.Parse(RANGES_START "0-", totalLength));
+ EXPECT_EQ(1U, ranges_all.Size());
+ EXPECT_TRUE(ranges_all.Get(0, range));
+ EXPECT_EQ(range0_, range);
+
+ CHttpRanges ranges_some;
+ EXPECT_TRUE(ranges_some.Parse(RANGES_START "2-", totalLength));
+ EXPECT_EQ(1U, ranges_some.Size());
+ EXPECT_TRUE(ranges_some.Get(0, range));
+ EXPECT_EQ(range2_, range);
+}
+
+TEST(TestHttpRanges, ParseFromEnd)
+{
+ const uint64_t totalLength = 5;
+ const CHttpRange range_1(totalLength - 1, totalLength - 1);
+ const CHttpRange range_3(totalLength - 3, totalLength - 1);
+
+ CHttpRange range;
+
+ CHttpRanges ranges_1;
+ EXPECT_TRUE(ranges_1.Parse(RANGES_START "-1", totalLength));
+ EXPECT_EQ(1U, ranges_1.Size());
+ EXPECT_TRUE(ranges_1.Get(0, range));
+ EXPECT_EQ(range_1, range);
+
+ CHttpRanges ranges_3;
+ EXPECT_TRUE(ranges_3.Parse(RANGES_START "-3", totalLength));
+ EXPECT_EQ(1U, ranges_3.Size());
+ EXPECT_TRUE(ranges_3.Get(0, range));
+ EXPECT_EQ(range_3, range);
+}
+
+TEST(TestHttpRanges, ParseSingle)
+{
+ const uint64_t totalLength = 5;
+ const CHttpRange range0_0(0, 0);
+ const CHttpRange range0_1(0, 1);
+ const CHttpRange range0_5(0, totalLength - 1);
+ const CHttpRange range1_1(1, 1);
+ const CHttpRange range1_3(1, 3);
+ const CHttpRange range3_4(3, 4);
+ const CHttpRange range4_4(4, 4);
+
+ CHttpRange range;
+
+ CHttpRanges ranges;
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-1", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-5", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_5, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "1-1", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range1_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "1-3", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range1_3, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "3-4", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range3_4, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "4-4", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range4_4, range);
+}
+
+TEST(TestHttpRanges, ParseMulti)
+{
+ const uint64_t totalLength = 6;
+ const CHttpRange range0_0(0, 0);
+ const CHttpRange range0_1(0, 1);
+ const CHttpRange range1_3(1, 3);
+ const CHttpRange range2_2(2, 2);
+ const CHttpRange range4_5(4, 5);
+ const CHttpRange range5_5(5, 5);
+
+ CHttpRange range;
+
+ CHttpRanges ranges;
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,2-2", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range2_2, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,2-2,4-5", totalLength));
+ EXPECT_EQ(3U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range2_2, range);
+ EXPECT_TRUE(ranges.Get(2, range));
+ EXPECT_EQ(range4_5, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-1,5-5", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_1, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range5_5, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "1-3,5-5", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range1_3, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range5_5, range);
+}
+
+TEST(TestHttpRanges, ParseOrderedNotOverlapping)
+{
+ const uint64_t totalLength = 5;
+ const CHttpRange range0_0(0, 0);
+ const CHttpRange range0_1(0, 1);
+ const CHttpRange range2_2(2, 2);
+ const CHttpRange range2_(2, totalLength - 1);
+ const CHttpRange range_1(totalLength - 1, totalLength - 1);
+
+ CHttpRange range;
+
+ CHttpRanges ranges;
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,-1", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,2-2,-1", totalLength));
+ EXPECT_EQ(3U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range2_2, range);
+ EXPECT_TRUE(ranges.Get(2, range));
+ EXPECT_EQ(range_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,2-", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range2_, range);
+}
+
+TEST(TestHttpRanges, ParseOrderedBackToBack)
+{
+ const uint64_t totalLength = 5;
+ const CHttpRange range0_1(0, 1);
+ const CHttpRange range0_2(0, 2);
+ const CHttpRange range1_2(1, 2);
+ const CHttpRange range0_3(0, 3);
+ const CHttpRange range4_4(4, 4);
+ const CHttpRange range0_4(0, 4);
+ const CHttpRange range3_4(3, 4);
+
+ CHttpRange range;
+
+ CHttpRanges ranges;
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,1-1", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,1-1,2-2", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_2, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,1-1,2-2,3-3", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_3, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,1-1,2-2,3-3,4-4", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_4, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,1-1,3-3,4-4", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_1, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range3_4, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "1-1,2-2,4-4", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range1_2, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range4_4, range);
+}
+
+TEST(TestHttpRanges, ParseOrderedOverlapping)
+{
+ const uint64_t totalLength = 5;
+ const CHttpRange range0_0(0, 0);
+ const CHttpRange range0_1(0, 1);
+ const CHttpRange range0_2(0, 2);
+ const CHttpRange range0_3(0, 3);
+ const CHttpRange range0_4(0, 4);
+ const CHttpRange range2_4(2, 4);
+
+ CHttpRange range;
+
+ CHttpRanges ranges;
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,0-1", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,0-1,0-2", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_2, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,0-1,1-2", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_2, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,0-2,1-3", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_3, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-1,1-2,2-3,3-4", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_4, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-0,2-3,2-4,4-4", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range2_4, range);
+}
+
+TEST(TestHttpRanges, ParseUnorderedNotOverlapping)
+{
+ const uint64_t totalLength = 5;
+ const CHttpRange range0_0(0, 0);
+ const CHttpRange range0_1(0, 1);
+ const CHttpRange range2_2(2, 2);
+ const CHttpRange range2_(2, totalLength - 1);
+ const CHttpRange range_1(totalLength - 1, totalLength - 1);
+
+ CHttpRange range;
+
+ CHttpRanges ranges;
+ EXPECT_TRUE(ranges.Parse(RANGES_START "-1,0-0", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "2-2,-1,0-0", totalLength));
+ EXPECT_EQ(3U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range2_2, range);
+ EXPECT_TRUE(ranges.Get(2, range));
+ EXPECT_EQ(range_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "2-,0-0", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range2_, range);
+}
+
+TEST(TestHttpRanges, ParseUnorderedBackToBack)
+{
+ const uint64_t totalLength = 5;
+ const CHttpRange range0_0(0, 0);
+ const CHttpRange range1_1(1, 1);
+ const CHttpRange range0_1(0, 1);
+ const CHttpRange range2_2(2, 2);
+ const CHttpRange range0_2(0, 2);
+ const CHttpRange range1_2(1, 2);
+ const CHttpRange range3_3(3, 3);
+ const CHttpRange range0_3(0, 3);
+ const CHttpRange range4_4(4, 4);
+ const CHttpRange range0_4(0, 4);
+ const CHttpRange range3_4(3, 4);
+
+ CHttpRange range;
+
+ CHttpRanges ranges;
+ EXPECT_TRUE(ranges.Parse(RANGES_START "1-1,0-0", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "1-1,0-0,2-2", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_2, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "2-2,1-1,3-3,0-0", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_3, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "4-4,1-1,0-0,2-2,3-3", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_4, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "3-3,0-0,4-4,1-1", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_1, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range3_4, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "4-4,1-1,2-2", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range1_2, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range4_4, range);
+}
+
+TEST(TestHttpRanges, ParseUnorderedOverlapping)
+{
+ const uint64_t totalLength = 5;
+ const CHttpRange range0_0(0, 0);
+ const CHttpRange range0_1(0, 1);
+ const CHttpRange range0_2(0, 2);
+ const CHttpRange range0_3(0, 3);
+ const CHttpRange range0_4(0, 4);
+ const CHttpRange range2_4(2, 4);
+
+ CHttpRange range;
+
+ CHttpRanges ranges;
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-1,0-0", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_1, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-2,0-0,0-1", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_2, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-1,1-2,0-0", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_2, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "0-2,0-0,1-3", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_3, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "2-3,1-2,0-1,3-4", totalLength));
+ EXPECT_EQ(1U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_4, range);
+
+ EXPECT_TRUE(ranges.Parse(RANGES_START "4-4,0-0,2-4,2-3", totalLength));
+ EXPECT_EQ(2U, ranges.Size());
+ EXPECT_TRUE(ranges.Get(0, range));
+ EXPECT_EQ(range0_0, range);
+ EXPECT_TRUE(ranges.Get(1, range));
+ EXPECT_EQ(range2_4, range);
+}
diff --git a/xbmc/utils/test/TestHttpResponse.cpp b/xbmc/utils/test/TestHttpResponse.cpp
new file mode 100644
index 0000000..1f66285
--- /dev/null
+++ b/xbmc/utils/test/TestHttpResponse.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/HttpResponse.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestHttpResponse, General)
+{
+ CHttpResponse a(HTTP::POST, HTTP::OK);
+ std::string response, content, refstr;
+
+ a.AddHeader("date", "Sun, 01 Jul 2012 00:00:00 -0400");
+ a.AddHeader("content-type", "text/html");
+ content = "<html>\r\n"
+ " <body>\r\n"
+ " <h1>XBMC TestHttpResponse Page</h1>\r\n"
+ " <p>blah blah blah</p>\r\n"
+ " </body>\r\n"
+ "</html>\r\n";
+ a.SetContent(content.c_str(), content.length());
+
+ response = a.Create();;
+ EXPECT_EQ((unsigned int)210, response.size());
+
+ refstr = "HTTP/1.1 200 OK\r\n"
+ "date: Sun, 01 Jul 2012 00:00:00 -0400\r\n"
+ "content-type: text/html\r\n"
+ "Content-Length: 106\r\n"
+ "\r\n"
+ "<html>\r\n"
+ " <body>\r\n"
+ " <h1>XBMC TestHttpResponse Page</h1>\r\n"
+ " <p>blah blah blah</p>\r\n"
+ " </body>\r\n"
+ "</html>\r\n";
+ EXPECT_STREQ(refstr.c_str(), response.c_str());
+}
diff --git a/xbmc/utils/test/TestJSONVariantParser.cpp b/xbmc/utils/test/TestJSONVariantParser.cpp
new file mode 100644
index 0000000..b8556b0
--- /dev/null
+++ b/xbmc/utils/test/TestJSONVariantParser.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/JSONVariantParser.h"
+#include "utils/Variant.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestJSONVariantParser, CannotParseNullptr)
+{
+ CVariant variant;
+ ASSERT_FALSE(CJSONVariantParser::Parse(nullptr, variant));
+}
+
+TEST(TestJSONVariantParser, CannotParseEmptyString)
+{
+ CVariant variant;
+ ASSERT_FALSE(CJSONVariantParser::Parse("", variant));
+ ASSERT_FALSE(CJSONVariantParser::Parse(std::string(), variant));
+}
+
+TEST(TestJSONVariantParser, CannotParseInvalidJson)
+{
+ CVariant variant;
+ ASSERT_FALSE(CJSONVariantParser::Parse("{", variant));
+ ASSERT_FALSE(CJSONVariantParser::Parse("}", variant));
+ ASSERT_FALSE(CJSONVariantParser::Parse("[", variant));
+ ASSERT_FALSE(CJSONVariantParser::Parse("]", variant));
+ ASSERT_FALSE(CJSONVariantParser::Parse("foo", variant));
+}
+
+TEST(TestJSONVariantParser, CanParseNull)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("null", variant));
+ ASSERT_TRUE(variant.isNull());
+}
+
+TEST(TestJSONVariantParser, CanParseBoolean)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("true", variant));
+ ASSERT_TRUE(variant.isBoolean());
+ ASSERT_TRUE(variant.asBoolean());
+
+ ASSERT_TRUE(CJSONVariantParser::Parse("false", variant));
+ ASSERT_TRUE(variant.isBoolean());
+ ASSERT_FALSE(variant.asBoolean());
+}
+
+TEST(TestJSONVariantParser, CanParseSignedInteger)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("-1", variant));
+ ASSERT_TRUE(variant.isInteger());
+ ASSERT_EQ(-1, variant.asInteger());
+}
+
+TEST(TestJSONVariantParser, CanParseUnsignedInteger)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("0", variant));
+ ASSERT_TRUE(variant.isUnsignedInteger());
+ ASSERT_EQ(0U, variant.asUnsignedInteger());
+
+ ASSERT_TRUE(CJSONVariantParser::Parse("1", variant));
+ ASSERT_TRUE(variant.isUnsignedInteger());
+ ASSERT_EQ(1U, variant.asUnsignedInteger());
+}
+
+TEST(TestJSONVariantParser, CanParseSignedInteger64)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("-4294967296", variant));
+ ASSERT_TRUE(variant.isInteger());
+ ASSERT_EQ(-4294967296, variant.asInteger());
+}
+
+TEST(TestJSONVariantParser, CanParseUnsignedInteger64)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("4294967296", variant));
+ ASSERT_TRUE(variant.isUnsignedInteger());
+ ASSERT_EQ(4294967296U, variant.asUnsignedInteger());
+}
+
+TEST(TestJSONVariantParser, CanParseDouble)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("0.0", variant));
+ ASSERT_TRUE(variant.isDouble());
+ ASSERT_EQ(0.0, variant.asDouble());
+
+ ASSERT_TRUE(CJSONVariantParser::Parse("1.0", variant));
+ ASSERT_TRUE(variant.isDouble());
+ ASSERT_EQ(1.0, variant.asDouble());
+
+ ASSERT_TRUE(CJSONVariantParser::Parse("-1.0", variant));
+ ASSERT_TRUE(variant.isDouble());
+ ASSERT_EQ(-1.0, variant.asDouble());
+}
+
+TEST(TestJSONVariantParser, CanParseString)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("\"\"", variant));
+ ASSERT_TRUE(variant.isString());
+ ASSERT_TRUE(variant.empty());
+
+ ASSERT_TRUE(CJSONVariantParser::Parse("\"foo\"", variant));
+ ASSERT_TRUE(variant.isString());
+ ASSERT_STREQ("foo", variant.asString().c_str());
+
+ ASSERT_TRUE(CJSONVariantParser::Parse("\"foo bar\"", variant));
+ ASSERT_TRUE(variant.isString());
+ ASSERT_STREQ("foo bar", variant.asString().c_str());
+}
+
+TEST(TestJSONVariantParser, CanParseObject)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("{}", variant));
+ ASSERT_TRUE(variant.isObject());
+ ASSERT_TRUE(variant.empty());
+
+ variant.clear();
+ ASSERT_TRUE(CJSONVariantParser::Parse("{ \"foo\": \"bar\" }", variant));
+ ASSERT_TRUE(variant.isObject());
+ ASSERT_TRUE(variant.isMember("foo"));
+ ASSERT_TRUE(variant["foo"].isString());
+ ASSERT_STREQ("bar", variant["foo"].asString().c_str());
+
+ variant.clear();
+ ASSERT_TRUE(CJSONVariantParser::Parse("{ \"foo\": \"bar\", \"bar\": true }", variant));
+ ASSERT_TRUE(variant.isObject());
+ ASSERT_TRUE(variant.isMember("foo"));
+ ASSERT_TRUE(variant["foo"].isString());
+ ASSERT_STREQ("bar", variant["foo"].asString().c_str());
+ ASSERT_TRUE(variant.isMember("bar"));
+ ASSERT_TRUE(variant["bar"].isBoolean());
+ ASSERT_TRUE(variant["bar"].asBoolean());
+
+ variant.clear();
+ ASSERT_TRUE(CJSONVariantParser::Parse("{ \"foo\": { \"sub-foo\": \"bar\" } }", variant));
+ ASSERT_TRUE(variant.isObject());
+ ASSERT_TRUE(variant.isMember("foo"));
+ ASSERT_TRUE(variant["foo"].isObject());
+ ASSERT_TRUE(variant["foo"].isMember("sub-foo"));
+ ASSERT_TRUE(variant["foo"]["sub-foo"].isString());
+ ASSERT_STREQ("bar", variant["foo"]["sub-foo"].asString().c_str());
+}
+
+TEST(TestJSONVariantParser, CanParseArray)
+{
+ CVariant variant;
+ ASSERT_TRUE(CJSONVariantParser::Parse("[]", variant));
+ ASSERT_TRUE(variant.isArray());
+ ASSERT_TRUE(variant.empty());
+
+ variant.clear();
+ ASSERT_TRUE(CJSONVariantParser::Parse("[ true ]", variant));
+ ASSERT_TRUE(variant.isArray());
+ ASSERT_EQ(1U, variant.size());
+ ASSERT_TRUE(variant[0].isBoolean());
+ ASSERT_TRUE(variant[0].asBoolean());
+
+ variant.clear();
+ ASSERT_TRUE(CJSONVariantParser::Parse("[ true, \"foo\" ]", variant));
+ ASSERT_TRUE(variant.isArray());
+ ASSERT_EQ(2U, variant.size());
+ ASSERT_TRUE(variant[0].isBoolean());
+ ASSERT_TRUE(variant[0].asBoolean());
+ ASSERT_TRUE(variant[1].isString());
+ ASSERT_STREQ("foo", variant[1].asString().c_str());
+
+ variant.clear();
+ ASSERT_TRUE(CJSONVariantParser::Parse("[ { \"foo\": \"bar\" } ]", variant));
+ ASSERT_TRUE(variant.isArray());
+ ASSERT_EQ(1U, variant.size());
+ ASSERT_TRUE(variant[0].isObject());
+ ASSERT_TRUE(variant[0].isMember("foo"));
+ ASSERT_TRUE(variant[0]["foo"].isString());
+ ASSERT_STREQ("bar", variant[0]["foo"].asString().c_str());
+}
diff --git a/xbmc/utils/test/TestJSONVariantWriter.cpp b/xbmc/utils/test/TestJSONVariantWriter.cpp
new file mode 100644
index 0000000..0772a4d
--- /dev/null
+++ b/xbmc/utils/test/TestJSONVariantWriter.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/JSONVariantWriter.h"
+#include "utils/Variant.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestJSONVariantWriter, CanWriteNull)
+{
+ CVariant variant;
+ std::string str;
+
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("null", str.c_str());
+}
+
+TEST(TestJSONVariantWriter, CanWriteBoolean)
+{
+ CVariant variant(true);
+ std::string str;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("true", str.c_str());
+
+ variant = false;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("false", str.c_str());
+}
+
+TEST(TestJSONVariantWriter, CanWriteSignedInteger)
+{
+ CVariant variant(-1);
+ std::string str;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("-1", str.c_str());
+}
+
+TEST(TestJSONVariantWriter, CanWriteUnsignedInteger)
+{
+ CVariant variant(0);
+ std::string str;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("0", str.c_str());
+
+ variant = 1;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("1", str.c_str());
+}
+
+TEST(TestJSONVariantWriter, CanWriteSignedInteger64)
+{
+ CVariant variant(static_cast<int64_t>(-4294967296LL));
+ std::string str;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("-4294967296", str.c_str());
+}
+
+TEST(TestJSONVariantWriter, CanWriteUnsignedInteger64)
+{
+ CVariant variant(static_cast<int64_t>(4294967296LL));
+ std::string str;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("4294967296", str.c_str());
+}
+
+TEST(TestJSONVariantWriter, CanWriteDouble)
+{
+ CVariant variant(0.0);
+ std::string str;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("0.0", str.c_str());
+
+ variant = 1.0;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("1.0", str.c_str());
+
+ variant = -1.0;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("-1.0", str.c_str());
+}
+
+TEST(TestJSONVariantWriter, CanWriteString)
+{
+ CVariant variant("");
+ std::string str;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("\"\"", str.c_str());
+
+ variant = "foo";
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("\"foo\"", str.c_str());
+
+ variant = "foo bar";
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("\"foo bar\"", str.c_str());
+}
+
+TEST(TestJSONVariantWriter, CanWriteObject)
+{
+ CVariant variant(CVariant::VariantTypeObject);
+ std::string str;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("{}", str.c_str());
+
+ variant.clear();
+ variant["foo"] = "bar";
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("{\n\t\"foo\": \"bar\"\n}", str.c_str());
+
+ variant.clear();
+ variant["foo"] = "bar";
+ variant["bar"] = true;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("{\n\t\"bar\": true,\n\t\"foo\": \"bar\"\n}", str.c_str());
+
+ variant.clear();
+ variant["foo"]["sub-foo"] = "bar";
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("{\n\t\"foo\": {\n\t\t\"sub-foo\": \"bar\"\n\t}\n}", str.c_str());
+}
+
+TEST(TestJSONVariantWriter, CanWriteArray)
+{
+ CVariant variant(CVariant::VariantTypeArray);
+ std::string str;
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("[]", str.c_str());
+
+ variant.clear();
+ variant.push_back(true);
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("[\n\ttrue\n]", str.c_str());
+
+ variant.clear();
+ variant.push_back(true);
+ variant.push_back("foo");
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("[\n\ttrue,\n\t\"foo\"\n]", str.c_str());
+
+ variant.clear();
+ CVariant obj(CVariant::VariantTypeObject);
+ obj["foo"] = "bar";
+ variant.push_back(obj);
+ ASSERT_TRUE(CJSONVariantWriter::Write(variant, str, false));
+ ASSERT_STREQ("[\n\t{\n\t\t\"foo\": \"bar\"\n\t}\n]", str.c_str());
+}
diff --git a/xbmc/utils/test/TestJobManager.cpp b/xbmc/utils/test/TestJobManager.cpp
new file mode 100644
index 0000000..86f0af9
--- /dev/null
+++ b/xbmc/utils/test/TestJobManager.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ServiceBroker.h"
+#include "test/MtTestUtils.h"
+#include "utils/Job.h"
+#include "utils/JobManager.h"
+#include "utils/XTimeUtils.h"
+
+#include <atomic>
+#include <mutex>
+
+#include <gtest/gtest.h>
+
+using namespace ConditionPoll;
+
+struct Flags
+{
+ std::atomic<bool> lingerAtWork{true};
+ std::atomic<bool> started{false};
+ std::atomic<bool> finished{false};
+ std::atomic<bool> wasCanceled{false};
+};
+
+class DummyJob : public CJob
+{
+ Flags* m_flags;
+public:
+ inline DummyJob(Flags* flags) : m_flags(flags)
+ {
+ }
+
+ bool DoWork() override
+ {
+ m_flags->started = true;
+ while (m_flags->lingerAtWork)
+ std::this_thread::yield();
+
+ if (ShouldCancel(0,0))
+ m_flags->wasCanceled = true;
+
+ m_flags->finished = true;
+ return true;
+ }
+};
+
+class ReallyDumbJob : public CJob
+{
+ Flags* m_flags;
+public:
+ inline ReallyDumbJob(Flags* flags) : m_flags(flags) {}
+
+ bool DoWork() override
+ {
+ m_flags->finished = true;
+ return true;
+ }
+};
+
+class TestJobManager : public testing::Test
+{
+protected:
+ TestJobManager() { CServiceBroker::RegisterJobManager(std::make_shared<CJobManager>()); }
+
+ ~TestJobManager() override
+ {
+ /* Always cancel jobs test completion */
+ CServiceBroker::GetJobManager()->CancelJobs();
+ CServiceBroker::GetJobManager()->Restart();
+ CServiceBroker::UnregisterJobManager();
+ }
+};
+
+TEST_F(TestJobManager, AddJob)
+{
+ Flags* flags = new Flags();
+ ReallyDumbJob* job = new ReallyDumbJob(flags);
+ CServiceBroker::GetJobManager()->AddJob(job, nullptr);
+ ASSERT_TRUE(poll([flags]() -> bool { return flags->finished; }));
+ delete flags;
+}
+
+TEST_F(TestJobManager, CancelJob)
+{
+ unsigned int id;
+ Flags* flags = new Flags();
+ DummyJob* job = new DummyJob(flags);
+ id = CServiceBroker::GetJobManager()->AddJob(job, nullptr);
+
+ // wait for the worker thread to be entered
+ ASSERT_TRUE(poll([flags]() -> bool { return flags->started; }));
+
+ // cancel the job
+ CServiceBroker::GetJobManager()->CancelJob(id);
+
+ // let the worker thread continue
+ flags->lingerAtWork = false;
+
+ // make sure the job finished.
+ ASSERT_TRUE(poll([flags]() -> bool { return flags->finished; }));
+
+ // ... and that it was canceled.
+ EXPECT_TRUE(flags->wasCanceled);
+ delete flags;
+}
+
+namespace
+{
+struct JobControlPackage
+{
+ JobControlPackage()
+ {
+ // We're not ready to wait yet
+ jobCreatedMutex.lock();
+ }
+
+ ~JobControlPackage()
+ {
+ jobCreatedMutex.unlock();
+ }
+
+ bool ready = false;
+ XbmcThreads::ConditionVariable jobCreatedCond;
+ CCriticalSection jobCreatedMutex;
+};
+
+class BroadcastingJob :
+ public CJob
+{
+public:
+
+ BroadcastingJob(JobControlPackage &package) :
+ m_package(package),
+ m_finish(false)
+ {
+ }
+
+ void FinishAndStopBlocking()
+ {
+ std::unique_lock<CCriticalSection> lock(m_blockMutex);
+
+ m_finish = true;
+ m_block.notifyAll();
+ }
+
+ const char * GetType() const override
+ {
+ return "BroadcastingJob";
+ }
+
+ bool DoWork() override
+ {
+ {
+ std::unique_lock<CCriticalSection> lock(m_package.jobCreatedMutex);
+
+ m_package.ready = true;
+ m_package.jobCreatedCond.notifyAll();
+ }
+
+ std::unique_lock<CCriticalSection> blockLock(m_blockMutex);
+
+ // Block until we're told to go away
+ while (!m_finish)
+ m_block.wait(m_blockMutex);
+ return true;
+ }
+
+private:
+
+ JobControlPackage &m_package;
+
+ XbmcThreads::ConditionVariable m_block;
+ CCriticalSection m_blockMutex;
+ bool m_finish;
+};
+
+BroadcastingJob *
+WaitForJobToStartProcessing(CJob::PRIORITY priority, JobControlPackage &package)
+{
+ BroadcastingJob* job = new BroadcastingJob(package);
+ CServiceBroker::GetJobManager()->AddJob(job, nullptr, priority);
+
+ // We're now ready to wait, wait and then unblock once ready
+ while (!package.ready)
+ package.jobCreatedCond.wait(package.jobCreatedMutex);
+
+ return job;
+}
+}
+
+TEST_F(TestJobManager, PauseLowPriorityJob)
+{
+ JobControlPackage package;
+ BroadcastingJob *job (WaitForJobToStartProcessing(CJob::PRIORITY_LOW_PAUSABLE, package));
+
+ EXPECT_TRUE(CServiceBroker::GetJobManager()->IsProcessing(CJob::PRIORITY_LOW_PAUSABLE));
+ CServiceBroker::GetJobManager()->PauseJobs();
+ EXPECT_FALSE(CServiceBroker::GetJobManager()->IsProcessing(CJob::PRIORITY_LOW_PAUSABLE));
+ CServiceBroker::GetJobManager()->UnPauseJobs();
+ EXPECT_TRUE(CServiceBroker::GetJobManager()->IsProcessing(CJob::PRIORITY_LOW_PAUSABLE));
+
+ job->FinishAndStopBlocking();
+}
+
+TEST_F(TestJobManager, IsProcessing)
+{
+ JobControlPackage package;
+ BroadcastingJob *job (WaitForJobToStartProcessing(CJob::PRIORITY_LOW_PAUSABLE, package));
+
+ EXPECT_EQ(0, CServiceBroker::GetJobManager()->IsProcessing(""));
+
+ job->FinishAndStopBlocking();
+}
diff --git a/xbmc/utils/test/TestLabelFormatter.cpp b/xbmc/utils/test/TestLabelFormatter.cpp
new file mode 100644
index 0000000..633e89a
--- /dev/null
+++ b/xbmc/utils/test/TestLabelFormatter.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "filesystem/File.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "test/TestUtils.h"
+#include "utils/LabelFormatter.h"
+
+#include <gtest/gtest.h>
+
+/* Set default settings used by CLabelFormatter. */
+class TestLabelFormatter : public testing::Test
+{
+protected:
+ TestLabelFormatter() = default;
+
+ ~TestLabelFormatter() override
+ {
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Unload();
+ }
+};
+
+TEST_F(TestLabelFormatter, FormatLabel)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath;
+ LABEL_MASKS labelMasks;
+ CLabelFormatter formatter("", labelMasks.m_strLabel2File);
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+
+ formatter.FormatLabel(item.get());
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+}
+
+TEST_F(TestLabelFormatter, FormatLabel2)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath;
+ LABEL_MASKS labelMasks;
+ CLabelFormatter formatter("", labelMasks.m_strLabel2File);
+
+ ASSERT_NE(nullptr, (tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+
+ formatter.FormatLabel2(item.get());
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+}
diff --git a/xbmc/utils/test/TestLangCodeExpander.cpp b/xbmc/utils/test/TestLangCodeExpander.cpp
new file mode 100644
index 0000000..7a6dde1
--- /dev/null
+++ b/xbmc/utils/test/TestLangCodeExpander.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/LangCodeExpander.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestLangCodeExpander, ConvertISO6391ToISO6392B)
+{
+ std::string refstr, varstr;
+
+ refstr = "eng";
+ g_LangCodeExpander.ConvertISO6391ToISO6392B("en", varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestLangCodeExpander, ConvertToISO6392B)
+{
+ std::string refstr, varstr;
+
+ refstr = "eng";
+ g_LangCodeExpander.ConvertToISO6392B("en", varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
diff --git a/xbmc/utils/test/TestLocale.cpp b/xbmc/utils/test/TestLocale.cpp
new file mode 100644
index 0000000..f5193ed
--- /dev/null
+++ b/xbmc/utils/test/TestLocale.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/Locale.h"
+#include "utils/StringUtils.h"
+
+#include <gtest/gtest.h>
+
+static const std::string TerritorySeparator = "_";
+static const std::string CodesetSeparator = ".";
+static const std::string ModifierSeparator = "@";
+
+static const std::string LanguageCodeEnglish = "en";
+static const std::string TerritoryCodeBritain = "GB";
+static const std::string CodesetUtf8 = "UTF-8";
+static const std::string ModifierLatin = "latin";
+
+TEST(TestLocale, DefaultLocale)
+{
+ CLocale locale;
+ ASSERT_FALSE(locale.IsValid());
+ ASSERT_STREQ("", locale.GetLanguageCode().c_str());
+ ASSERT_STREQ("", locale.GetTerritoryCode().c_str());
+ ASSERT_STREQ("", locale.GetCodeset().c_str());
+ ASSERT_STREQ("", locale.GetModifier().c_str());
+ ASSERT_STREQ("", locale.ToString().c_str());
+}
+
+TEST(TestLocale, LanguageLocale)
+{
+ CLocale locale(LanguageCodeEnglish);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.GetLanguageCode().c_str());
+ ASSERT_STREQ("", locale.GetTerritoryCode().c_str());
+ ASSERT_STREQ("", locale.GetCodeset().c_str());
+ ASSERT_STREQ("", locale.GetModifier().c_str());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.ToString().c_str());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.ToStringLC().c_str());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.ToShortString().c_str());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.ToShortStringLC().c_str());
+}
+
+TEST(TestLocale, LanguageTerritoryLocale)
+{
+ const std::string strLocale = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain;
+ std::string strLocaleLC = strLocale;
+ StringUtils::ToLower(strLocaleLC);
+
+ CLocale locale(LanguageCodeEnglish, TerritoryCodeBritain);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.GetLanguageCode().c_str());
+ ASSERT_STREQ(TerritoryCodeBritain.c_str(), locale.GetTerritoryCode().c_str());
+ ASSERT_STREQ("", locale.GetCodeset().c_str());
+ ASSERT_STREQ("", locale.GetModifier().c_str());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+ ASSERT_STREQ(strLocaleLC.c_str(), locale.ToStringLC().c_str());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToShortString().c_str());
+ ASSERT_STREQ(strLocaleLC.c_str(), locale.ToShortStringLC().c_str());
+}
+
+TEST(TestLocale, LanguageCodesetLocale)
+{
+ const std::string strLocale = LanguageCodeEnglish + CodesetSeparator + CodesetUtf8;
+ std::string strLocaleLC = strLocale;
+ StringUtils::ToLower(strLocaleLC);
+
+ CLocale locale(LanguageCodeEnglish, "", CodesetUtf8);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.GetLanguageCode().c_str());
+ ASSERT_STREQ("", locale.GetTerritoryCode().c_str());
+ ASSERT_STREQ(CodesetUtf8.c_str(), locale.GetCodeset().c_str());
+ ASSERT_STREQ("", locale.GetModifier().c_str());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+ ASSERT_STREQ(strLocaleLC.c_str(), locale.ToStringLC().c_str());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.ToShortString().c_str());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.ToShortStringLC().c_str());
+}
+
+TEST(TestLocale, LanguageModifierLocale)
+{
+ const std::string strLocale = LanguageCodeEnglish + ModifierSeparator + ModifierLatin;
+ std::string strLocaleLC = strLocale;
+ StringUtils::ToLower(strLocaleLC);
+
+ CLocale locale(LanguageCodeEnglish, "", "", ModifierLatin);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.GetLanguageCode().c_str());
+ ASSERT_STREQ("", locale.GetTerritoryCode().c_str());
+ ASSERT_STREQ("", locale.GetCodeset().c_str());
+ ASSERT_STREQ(ModifierLatin.c_str(), locale.GetModifier().c_str());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+ ASSERT_STREQ(strLocaleLC.c_str(), locale.ToStringLC().c_str());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.ToShortString().c_str());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.ToShortStringLC().c_str());
+}
+
+TEST(TestLocale, LanguageTerritoryCodesetLocale)
+{
+ const std::string strLocaleShort = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain;
+ std::string strLocaleShortLC = strLocaleShort;
+ StringUtils::ToLower(strLocaleShortLC);
+ const std::string strLocale = strLocaleShort + CodesetSeparator + CodesetUtf8;
+ std::string strLocaleLC = strLocale;
+ StringUtils::ToLower(strLocaleLC);
+
+ CLocale locale(LanguageCodeEnglish, TerritoryCodeBritain, CodesetUtf8);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.GetLanguageCode().c_str());
+ ASSERT_STREQ(TerritoryCodeBritain.c_str(), locale.GetTerritoryCode().c_str());
+ ASSERT_STREQ(CodesetUtf8.c_str(), locale.GetCodeset().c_str());
+ ASSERT_STREQ("", locale.GetModifier().c_str());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+ ASSERT_STREQ(strLocaleLC.c_str(), locale.ToStringLC().c_str());
+ ASSERT_STREQ(strLocaleShort.c_str(), locale.ToShortString().c_str());
+ ASSERT_STREQ(strLocaleShortLC.c_str(), locale.ToShortStringLC().c_str());
+}
+
+TEST(TestLocale, LanguageTerritoryModifierLocale)
+{
+ const std::string strLocaleShort = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain;
+ std::string strLocaleShortLC = strLocaleShort;
+ StringUtils::ToLower(strLocaleShortLC);
+ const std::string strLocale = strLocaleShort + ModifierSeparator + ModifierLatin;
+ std::string strLocaleLC = strLocale;
+ StringUtils::ToLower(strLocaleLC);
+
+ CLocale locale(LanguageCodeEnglish, TerritoryCodeBritain, "", ModifierLatin);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.GetLanguageCode().c_str());
+ ASSERT_STREQ(TerritoryCodeBritain.c_str(), locale.GetTerritoryCode().c_str());
+ ASSERT_STREQ("", locale.GetCodeset().c_str());
+ ASSERT_STREQ(ModifierLatin.c_str(), locale.GetModifier().c_str());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+ ASSERT_STREQ(strLocaleLC.c_str(), locale.ToStringLC().c_str());
+ ASSERT_STREQ(strLocaleShort.c_str(), locale.ToShortString().c_str());
+ ASSERT_STREQ(strLocaleShortLC.c_str(), locale.ToShortStringLC().c_str());
+}
+
+TEST(TestLocale, LanguageTerritoryCodesetModifierLocale)
+{
+ const std::string strLocaleShort = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain;
+ std::string strLocaleShortLC = strLocaleShort;
+ StringUtils::ToLower(strLocaleShortLC);
+ const std::string strLocale = strLocaleShort + CodesetSeparator + CodesetUtf8 + ModifierSeparator + ModifierLatin;
+ std::string strLocaleLC = strLocale;
+ StringUtils::ToLower(strLocaleLC);
+
+ CLocale locale(LanguageCodeEnglish, TerritoryCodeBritain, CodesetUtf8, ModifierLatin);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.GetLanguageCode().c_str());
+ ASSERT_STREQ(TerritoryCodeBritain.c_str(), locale.GetTerritoryCode().c_str());
+ ASSERT_STREQ(CodesetUtf8.c_str(), locale.GetCodeset().c_str());
+ ASSERT_STREQ(ModifierLatin.c_str(), locale.GetModifier().c_str());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+ ASSERT_STREQ(strLocaleLC.c_str(), locale.ToStringLC().c_str());
+ ASSERT_STREQ(strLocaleShort.c_str(), locale.ToShortString().c_str());
+ ASSERT_STREQ(strLocaleShortLC.c_str(), locale.ToShortStringLC().c_str());
+}
+
+TEST(TestLocale, FullStringLocale)
+{
+ const std::string strLocaleShort = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain;
+ std::string strLocaleShortLC = strLocaleShort;
+ StringUtils::ToLower(strLocaleShortLC);
+ const std::string strLocale = strLocaleShort + CodesetSeparator + CodesetUtf8 + ModifierSeparator + ModifierLatin;
+ std::string strLocaleLC = strLocale;
+ StringUtils::ToLower(strLocaleLC);
+
+ CLocale locale(strLocale);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(LanguageCodeEnglish.c_str(), locale.GetLanguageCode().c_str());
+ ASSERT_STREQ(TerritoryCodeBritain.c_str(), locale.GetTerritoryCode().c_str());
+ ASSERT_STREQ(CodesetUtf8.c_str(), locale.GetCodeset().c_str());
+ ASSERT_STREQ(ModifierLatin.c_str(), locale.GetModifier().c_str());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+ ASSERT_STREQ(strLocaleLC.c_str(), locale.ToStringLC().c_str());
+ ASSERT_STREQ(strLocaleShort.c_str(), locale.ToShortString().c_str());
+ ASSERT_STREQ(strLocaleShortLC.c_str(), locale.ToShortStringLC().c_str());
+}
+
+TEST(TestLocale, FromString)
+{
+ std::string strLocale = "";
+ CLocale locale = CLocale::FromString(strLocale);
+ ASSERT_FALSE(locale.IsValid());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+
+ strLocale = LanguageCodeEnglish;
+ locale = CLocale::FromString(strLocale);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+
+ strLocale = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain;
+ locale = CLocale::FromString(strLocale);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+
+ strLocale = LanguageCodeEnglish + CodesetSeparator + CodesetUtf8;
+ locale = CLocale::FromString(strLocale);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+
+ strLocale = LanguageCodeEnglish + ModifierSeparator + ModifierLatin;
+ locale = CLocale::FromString(strLocale);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+
+ strLocale = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain + CodesetSeparator + CodesetUtf8;
+ locale = CLocale::FromString(strLocale);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+
+ strLocale = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain + ModifierSeparator + ModifierLatin;
+ locale = CLocale::FromString(strLocale);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+
+ strLocale = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain + CodesetSeparator + CodesetUtf8 + ModifierSeparator + ModifierLatin;
+ locale = CLocale::FromString(strLocale);
+ ASSERT_TRUE(locale.IsValid());
+ ASSERT_STREQ(strLocale.c_str(), locale.ToString().c_str());
+}
+
+TEST(TestLocale, EmptyLocale)
+{
+ ASSERT_FALSE(CLocale::Empty.IsValid());
+ ASSERT_STREQ("", CLocale::Empty.GetLanguageCode().c_str());
+ ASSERT_STREQ("", CLocale::Empty.GetTerritoryCode().c_str());
+ ASSERT_STREQ("", CLocale::Empty.GetCodeset().c_str());
+ ASSERT_STREQ("", CLocale::Empty.GetModifier().c_str());
+ ASSERT_STREQ("", CLocale::Empty.ToString().c_str());
+}
+
+TEST(TestLocale, Equals)
+{
+ std::string strLocale = "";
+ CLocale locale;
+ ASSERT_TRUE(locale.Equals(strLocale));
+
+ locale = CLocale(LanguageCodeEnglish);
+ strLocale = LanguageCodeEnglish;
+ ASSERT_TRUE(locale.Equals(strLocale));
+
+ locale = CLocale(LanguageCodeEnglish, TerritoryCodeBritain);
+ strLocale = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain;
+ ASSERT_TRUE(locale.Equals(strLocale));
+
+ locale = CLocale(LanguageCodeEnglish, "", CodesetUtf8);
+ strLocale = LanguageCodeEnglish + CodesetSeparator + CodesetUtf8;
+ ASSERT_TRUE(locale.Equals(strLocale));
+
+ locale = CLocale(LanguageCodeEnglish, "", "", ModifierLatin);
+ strLocale = LanguageCodeEnglish + ModifierSeparator + ModifierLatin;
+ ASSERT_TRUE(locale.Equals(strLocale));
+
+ locale = CLocale(LanguageCodeEnglish, TerritoryCodeBritain, CodesetUtf8);
+ strLocale = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain + CodesetSeparator + CodesetUtf8;
+ ASSERT_TRUE(locale.Equals(strLocale));
+
+ locale = CLocale(LanguageCodeEnglish, TerritoryCodeBritain, "", ModifierLatin);
+ strLocale = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain + ModifierSeparator + ModifierLatin;
+ ASSERT_TRUE(locale.Equals(strLocale));
+
+ locale = CLocale(LanguageCodeEnglish, TerritoryCodeBritain, CodesetUtf8, ModifierLatin);
+ strLocale = LanguageCodeEnglish + TerritorySeparator + TerritoryCodeBritain + CodesetSeparator + CodesetUtf8 + ModifierSeparator + ModifierLatin;
+ ASSERT_TRUE(locale.Equals(strLocale));
+}
diff --git a/xbmc/utils/test/TestMathUtils.cpp b/xbmc/utils/test/TestMathUtils.cpp
new file mode 100644
index 0000000..d60cc3f
--- /dev/null
+++ b/xbmc/utils/test/TestMathUtils.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/MathUtils.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestMathUtils, round_int)
+{
+ int refval, varval, i;
+
+ for (i = -8; i < 8; ++i)
+ {
+ double d = 0.25*i;
+ refval = (i < 0) ? (i - 1) / 4 : (i + 2) / 4;
+ varval = MathUtils::round_int(d);
+ EXPECT_EQ(refval, varval);
+ }
+}
+
+TEST(TestMathUtils, truncate_int)
+{
+ int refval, varval, i;
+
+ for (i = -8; i < 8; ++i)
+ {
+ double d = 0.25*i;
+ refval = i / 4;
+ varval = MathUtils::truncate_int(d);
+ EXPECT_EQ(refval, varval);
+ }
+}
+
+TEST(TestMathUtils, abs)
+{
+ int64_t refval, varval;
+
+ refval = 5;
+ varval = MathUtils::abs(-5);
+ EXPECT_EQ(refval, varval);
+}
+
+TEST(TestMathUtils, bitcount)
+{
+ unsigned refval, varval;
+
+ refval = 10;
+ varval = MathUtils::bitcount(0x03FF);
+ EXPECT_EQ(refval, varval);
+
+ refval = 8;
+ varval = MathUtils::bitcount(0x2AD5);
+ EXPECT_EQ(refval, varval);
+}
diff --git a/xbmc/utils/test/TestMime.cpp b/xbmc/utils/test/TestMime.cpp
new file mode 100644
index 0000000..7ef82c3
--- /dev/null
+++ b/xbmc/utils/test/TestMime.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "FileItem.h"
+#include "utils/Mime.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestMime, GetMimeType_string)
+{
+ EXPECT_STREQ("video/avi", CMime::GetMimeType("avi").c_str());
+ EXPECT_STRNE("video/x-msvideo", CMime::GetMimeType("avi").c_str());
+ EXPECT_STRNE("video/avi", CMime::GetMimeType("xvid").c_str());
+}
+
+TEST(TestMime, GetMimeType_CFileItem)
+{
+ std::string refstr, varstr;
+ CFileItem item("testfile.mp4", false);
+
+ refstr = "video/mp4";
+ varstr = CMime::GetMimeType(item);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
diff --git a/xbmc/utils/test/TestPOUtils.cpp b/xbmc/utils/test/TestPOUtils.cpp
new file mode 100644
index 0000000..5808c31
--- /dev/null
+++ b/xbmc/utils/test/TestPOUtils.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "test/TestUtils.h"
+#include "utils/POUtils.h"
+
+#include <gtest/gtest.h>
+
+
+TEST(TestPOUtils, General)
+{
+ CPODocument a;
+
+ EXPECT_TRUE(a.LoadFile(XBMC_REF_FILE_PATH("xbmc/utils/test/data/language/Spanish/strings.po")));
+
+ EXPECT_TRUE(a.GetNextEntry());
+ EXPECT_EQ(ID_FOUND, a.GetEntryType());
+ EXPECT_EQ((uint32_t)0, a.GetEntryID());
+ a.ParseEntry(false);
+ EXPECT_STREQ("", a.GetMsgctxt().c_str());
+ EXPECT_STREQ("Programs", a.GetMsgid().c_str());
+ EXPECT_STREQ("Programas", a.GetMsgstr().c_str());
+ EXPECT_STREQ("", a.GetPlurMsgstr(0).c_str());
+
+ EXPECT_TRUE(a.GetNextEntry());
+ EXPECT_EQ(ID_FOUND, a.GetEntryType());
+ EXPECT_EQ((uint32_t)1, a.GetEntryID());
+ a.ParseEntry(false);
+ EXPECT_STREQ("", a.GetMsgctxt().c_str());
+ EXPECT_STREQ("Pictures", a.GetMsgid().c_str());
+ EXPECT_STREQ("Imágenes", a.GetMsgstr().c_str());
+ EXPECT_STREQ("", a.GetPlurMsgstr(0).c_str());
+
+ EXPECT_TRUE(a.GetNextEntry());
+ EXPECT_EQ(ID_FOUND, a.GetEntryType());
+ EXPECT_EQ((uint32_t)2, a.GetEntryID());
+ a.ParseEntry(false);
+ EXPECT_STREQ("", a.GetMsgctxt().c_str());
+ EXPECT_STREQ("Music", a.GetMsgid().c_str());
+ EXPECT_STREQ("Música", a.GetMsgstr().c_str());
+ EXPECT_STREQ("", a.GetPlurMsgstr(0).c_str());
+}
diff --git a/xbmc/utils/test/TestRegExp.cpp b/xbmc/utils/test/TestRegExp.cpp
new file mode 100644
index 0000000..1cd3939
--- /dev/null
+++ b/xbmc/utils/test/TestRegExp.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+/** @todo gtest/gtest.h needs to come in before utils/RegExp.h.
+ * Investigate why.
+ */
+#include "CompileInfo.h"
+#include "ServiceBroker.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "utils/RegExp.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestRegExp, RegFind)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^Test.*"));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+
+ EXPECT_TRUE(regex.RegComp("^string.*"));
+ EXPECT_EQ(-1, regex.RegFind("Test string."));
+}
+
+TEST(TestRegExp, GetReplaceString)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_STREQ("string", regex.GetReplaceString("\\2").c_str());
+}
+
+TEST(TestRegExp, GetFindLen)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_EQ(12, regex.GetFindLen());
+}
+
+TEST(TestRegExp, GetSubCount)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_EQ(2, regex.GetSubCount());
+}
+
+TEST(TestRegExp, GetSubStart)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_EQ(0, regex.GetSubStart(0));
+ EXPECT_EQ(0, regex.GetSubStart(1));
+ EXPECT_EQ(5, regex.GetSubStart(2));
+}
+
+TEST(TestRegExp, GetCaptureTotal)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_EQ(2, regex.GetCaptureTotal());
+}
+
+TEST(TestRegExp, GetMatch)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_STREQ("Test string.", regex.GetMatch(0).c_str());
+ EXPECT_STREQ("Test", regex.GetMatch(1).c_str());
+ EXPECT_STREQ("string", regex.GetMatch(2).c_str());
+}
+
+TEST(TestRegExp, GetPattern)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_STREQ("^(Test)\\s*(.*)\\.", regex.GetPattern().c_str());
+}
+
+TEST(TestRegExp, GetNamedSubPattern)
+{
+ CRegExp regex;
+ std::string match;
+
+ EXPECT_TRUE(regex.RegComp("^(?<first>Test)\\s*(?<second>.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_TRUE(regex.GetNamedSubPattern("first", match));
+ EXPECT_STREQ("Test", match.c_str());
+ EXPECT_TRUE(regex.GetNamedSubPattern("second", match));
+ EXPECT_STREQ("string", match.c_str());
+}
+
+TEST(TestRegExp, operatorEqual)
+{
+ CRegExp regex, regexcopy;
+ std::string match;
+
+ EXPECT_TRUE(regex.RegComp("^(?<first>Test)\\s*(?<second>.*)\\."));
+ regexcopy = regex;
+ EXPECT_EQ(0, regexcopy.RegFind("Test string."));
+ EXPECT_TRUE(regexcopy.GetNamedSubPattern("first", match));
+ EXPECT_STREQ("Test", match.c_str());
+ EXPECT_TRUE(regexcopy.GetNamedSubPattern("second", match));
+ EXPECT_STREQ("string", match.c_str());
+}
+
+class TestRegExpLog : public testing::Test
+{
+protected:
+ TestRegExpLog() = default;
+ ~TestRegExpLog() override { CServiceBroker::GetLogging().Deinitialize(); }
+};
+
+TEST_F(TestRegExpLog, DumpOvector)
+{
+ CRegExp regex;
+ std::string logfile, logstring;
+ char buf[100];
+ ssize_t bytesread;
+ XFILE::CFile file;
+
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
+ CServiceBroker::GetLogging().Initialize(
+ CSpecialProtocol::TranslatePath("special://temp/").c_str());
+ EXPECT_TRUE(XFILE::CFile::Exists(logfile));
+
+ EXPECT_TRUE(regex.RegComp("^(?<first>Test)\\s*(?<second>.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ regex.DumpOvector(LOGDEBUG);
+ CServiceBroker::GetLogging().Deinitialize();
+
+ EXPECT_TRUE(file.Open(logfile));
+ while ((bytesread = file.Read(buf, sizeof(buf) - 1)) > 0)
+ {
+ buf[bytesread] = '\0';
+ logstring.append(buf);
+ }
+ file.Close();
+ EXPECT_FALSE(logstring.empty());
+
+ EXPECT_STREQ("\xEF\xBB\xBF", logstring.substr(0, 3).c_str());
+
+ EXPECT_TRUE(regex.RegComp(".*(debug|DEBUG) <general>: regexp ovector=\\{\\[0,12\\],\\[0,4\\],"
+ "\\[5,11\\]\\}.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+
+ EXPECT_TRUE(XFILE::CFile::Delete(logfile));
+}
diff --git a/xbmc/utils/test/TestRingBuffer.cpp b/xbmc/utils/test/TestRingBuffer.cpp
new file mode 100644
index 0000000..e2fd2d5
--- /dev/null
+++ b/xbmc/utils/test/TestRingBuffer.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/RingBuffer.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestRingBuffer, General)
+{
+ CRingBuffer a;
+ char data[20];
+ unsigned int i;
+
+ EXPECT_TRUE(a.Create(20));
+ EXPECT_EQ((unsigned int)20, a.getSize());
+ memset(data, 0, sizeof(data));
+ for (i = 0; i < a.getSize(); i++)
+ EXPECT_TRUE(a.WriteData(data, 1));
+ a.Clear();
+
+ memcpy(data, "0123456789", sizeof("0123456789"));
+ EXPECT_TRUE(a.WriteData(data, sizeof("0123456789")));
+ EXPECT_STREQ("0123456789", a.getBuffer());
+
+ memset(data, 0, sizeof(data));
+ EXPECT_TRUE(a.ReadData(data, 5));
+ EXPECT_STREQ("01234", data);
+}
diff --git a/xbmc/utils/test/TestScraperParser.cpp b/xbmc/utils/test/TestScraperParser.cpp
new file mode 100644
index 0000000..4ff4b06
--- /dev/null
+++ b/xbmc/utils/test/TestScraperParser.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "test/TestUtils.h"
+#include "utils/ScraperParser.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestScraperParser, General)
+{
+ CScraperParser a;
+
+ a.Clear();
+ EXPECT_TRUE(a.Load(XBMC_REF_FILE_PATH("/addons/metadata.local/local.xml")));
+
+ EXPECT_STREQ(XBMC_REF_FILE_PATH("/addons/metadata.local/local.xml").c_str(),
+ a.GetFilename().c_str());
+ EXPECT_STREQ("UTF-8", a.GetSearchStringEncoding().c_str());
+}
diff --git a/xbmc/utils/test/TestScraperUrl.cpp b/xbmc/utils/test/TestScraperUrl.cpp
new file mode 100644
index 0000000..1feb181
--- /dev/null
+++ b/xbmc/utils/test/TestScraperUrl.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/ScraperUrl.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestScraperUrl, General)
+{
+ CScraperUrl a;
+ std::string xmlstring;
+
+ xmlstring = "<data spoof=\"blah\" gzip=\"yes\">\n"
+ " <someurl>\n"
+ " </someurl>\n"
+ " <someotherurl>\n"
+ " </someotherurl>\n"
+ "</data>\n";
+ EXPECT_TRUE(a.ParseFromData(xmlstring));
+
+ const auto url = a.GetFirstUrlByType();
+ EXPECT_STREQ("blah", url.m_spoof.c_str());
+ EXPECT_STREQ("someurl", url.m_url.c_str());
+ EXPECT_STREQ("", url.m_cache.c_str());
+ EXPECT_EQ(CScraperUrl::UrlType::General, url.m_type);
+ EXPECT_FALSE(url.m_post);
+ EXPECT_TRUE(url.m_isgz);
+ EXPECT_EQ(-1, url.m_season);
+}
diff --git a/xbmc/utils/test/TestSortUtils.cpp b/xbmc/utils/test/TestSortUtils.cpp
new file mode 100644
index 0000000..dac3c62
--- /dev/null
+++ b/xbmc/utils/test/TestSortUtils.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/SortUtils.h"
+#include "utils/Variant.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestSortUtils, Sort_SortBy)
+{
+ SortItems items;
+
+ CVariant variant1("M Artist");
+ SortItemPtr item1(new SortItem());
+ (*item1)[FieldArtist] = variant1;
+ CVariant variant2("B Artist");
+ SortItemPtr item2(new SortItem());
+ (*item2)[FieldArtist] = variant2;
+ CVariant variant3("R Artist");
+ SortItemPtr item3(new SortItem());
+ (*item3)[FieldArtist] = variant3;
+ CVariant variant4("R Artist");
+ SortItemPtr item4(new SortItem());
+ (*item4)[FieldArtist] = variant4;
+ CVariant variant5("I Artist");
+ SortItemPtr item5(new SortItem());
+ (*item5)[FieldArtist] = variant5;
+ CVariant variant6("A Artist");
+ SortItemPtr item6(new SortItem());
+ (*item6)[FieldArtist] = variant6;
+ CVariant variant7("G Artist");
+ SortItemPtr item7(new SortItem());
+ (*item7)[FieldArtist] = variant7;
+
+ items.push_back(item1);
+ items.push_back(item2);
+ items.push_back(item3);
+ items.push_back(item4);
+ items.push_back(item5);
+ items.push_back(item6);
+ items.push_back(item7);
+
+ SortUtils::Sort(SortByArtist, SortOrderAscending, SortAttributeNone, items);
+
+ EXPECT_STREQ("A Artist", (*items.at(0))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("B Artist", (*items.at(1))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("G Artist", (*items.at(2))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("I Artist", (*items.at(3))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("M Artist", (*items.at(4))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("R Artist", (*items.at(5))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("R Artist", (*items.at(6))[FieldArtist].asString().c_str());
+}
+
+TEST(TestSortUtils, Sort_SortDescription)
+{
+ SortItems items;
+
+ CVariant variant1("M Artist");
+ SortItemPtr item1(new SortItem());
+ (*item1)[FieldArtist] = variant1;
+ CVariant variant2("B Artist");
+ SortItemPtr item2(new SortItem());
+ (*item2)[FieldArtist] = variant2;
+ CVariant variant3("R Artist");
+ SortItemPtr item3(new SortItem());
+ (*item3)[FieldArtist] = variant3;
+ CVariant variant4("R Artist");
+ SortItemPtr item4(new SortItem());
+ (*item4)[FieldArtist] = variant4;
+ CVariant variant5("I Artist");
+ SortItemPtr item5(new SortItem());
+ (*item5)[FieldArtist] = variant5;
+ CVariant variant6("A Artist");
+ SortItemPtr item6(new SortItem());
+ (*item6)[FieldArtist] = variant6;
+ CVariant variant7("G Artist");
+ SortItemPtr item7(new SortItem());
+ (*item7)[FieldArtist] = variant7;
+
+ items.push_back(item1);
+ items.push_back(item2);
+ items.push_back(item3);
+ items.push_back(item4);
+ items.push_back(item5);
+ items.push_back(item6);
+ items.push_back(item7);
+
+ SortDescription desc;
+ desc.sortBy = SortByArtist;
+ SortUtils::Sort(desc, items);
+
+ EXPECT_STREQ("A Artist", (*items.at(0))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("B Artist", (*items.at(1))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("G Artist", (*items.at(2))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("I Artist", (*items.at(3))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("M Artist", (*items.at(4))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("R Artist", (*items.at(5))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("R Artist", (*items.at(6))[FieldArtist].asString().c_str());
+}
+
+TEST(TestSortUtils, GetFieldsForSorting)
+{
+ Fields fields;
+
+ fields = SortUtils::GetFieldsForSorting(SortByArtist);
+ Fields::iterator it;
+ it = fields.find(FieldAlbum);
+ EXPECT_EQ(FieldAlbum, *it);
+ it = fields.find(FieldArtist);
+ EXPECT_EQ(FieldArtist, *it);
+ it = fields.find(FieldArtistSort);
+ EXPECT_EQ(FieldArtistSort, *it);
+ it = fields.find(FieldYear);
+ EXPECT_EQ(FieldYear, *it);
+ it = fields.find(FieldTrackNumber);
+ EXPECT_EQ(FieldTrackNumber, *it);
+ EXPECT_EQ((unsigned int)5, fields.size());
+}
diff --git a/xbmc/utils/test/TestStopwatch.cpp b/xbmc/utils/test/TestStopwatch.cpp
new file mode 100644
index 0000000..82f555d
--- /dev/null
+++ b/xbmc/utils/test/TestStopwatch.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "threads/Thread.h"
+#include "utils/Stopwatch.h"
+
+#include <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+
+class CTestStopWatchThread : public CThread
+{
+public:
+ CTestStopWatchThread() :
+ CThread("TestStopWatch"){}
+};
+
+TEST(TestStopWatch, Initialization)
+{
+ CStopWatch a;
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_EQ(0.0f, a.GetElapsedSeconds());
+ EXPECT_EQ(0.0f, a.GetElapsedMilliseconds());
+}
+
+TEST(TestStopWatch, Start)
+{
+ CStopWatch a;
+ a.Start();
+ EXPECT_TRUE(a.IsRunning());
+}
+
+TEST(TestStopWatch, Stop)
+{
+ CStopWatch a;
+ a.Start();
+ a.Stop();
+ EXPECT_FALSE(a.IsRunning());
+}
+
+TEST(TestStopWatch, ElapsedTime)
+{
+ CStopWatch a;
+ CTestStopWatchThread thread;
+ a.Start();
+ thread.Sleep(1ms);
+ EXPECT_GT(a.GetElapsedSeconds(), 0.0f);
+ EXPECT_GT(a.GetElapsedMilliseconds(), 0.0f);
+}
+
+TEST(TestStopWatch, Reset)
+{
+ CStopWatch a;
+ CTestStopWatchThread thread;
+ a.StartZero();
+ thread.Sleep(2ms);
+ EXPECT_GT(a.GetElapsedMilliseconds(), 1);
+ thread.Sleep(3ms);
+ a.Reset();
+ EXPECT_LT(a.GetElapsedMilliseconds(), 5);
+}
diff --git a/xbmc/utils/test/TestStreamDetails.cpp b/xbmc/utils/test/TestStreamDetails.cpp
new file mode 100644
index 0000000..7842eee
--- /dev/null
+++ b/xbmc/utils/test/TestStreamDetails.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/StreamDetails.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestStreamDetails, General)
+{
+ CStreamDetails a;
+ CStreamDetailVideo *video = new CStreamDetailVideo();
+ CStreamDetailAudio *audio = new CStreamDetailAudio();
+ CStreamDetailSubtitle *subtitle = new CStreamDetailSubtitle();
+
+ video->m_iWidth = 1920;
+ video->m_iHeight = 1080;
+ video->m_fAspect = 2.39f;
+ video->m_iDuration = 30;
+ video->m_strCodec = "h264";
+ video->m_strStereoMode = "left_right";
+ video->m_strLanguage = "eng";
+
+ audio->m_iChannels = 2;
+ audio->m_strCodec = "aac";
+ audio->m_strLanguage = "eng";
+
+ subtitle->m_strLanguage = "eng";
+
+ a.AddStream(video);
+ a.AddStream(audio);
+
+ EXPECT_TRUE(a.HasItems());
+
+ EXPECT_EQ(1, a.GetStreamCount(CStreamDetail::VIDEO));
+ EXPECT_EQ(1, a.GetVideoStreamCount());
+ EXPECT_STREQ("", a.GetVideoCodec().c_str());
+ EXPECT_EQ(0.0f, a.GetVideoAspect());
+ EXPECT_EQ(0, a.GetVideoWidth());
+ EXPECT_EQ(0, a.GetVideoHeight());
+ EXPECT_EQ(0, a.GetVideoDuration());
+ EXPECT_STREQ("", a.GetStereoMode().c_str());
+
+ EXPECT_EQ(1, a.GetStreamCount(CStreamDetail::AUDIO));
+ EXPECT_EQ(1, a.GetAudioStreamCount());
+
+ EXPECT_EQ(0, a.GetStreamCount(CStreamDetail::SUBTITLE));
+ EXPECT_EQ(0, a.GetSubtitleStreamCount());
+
+ a.AddStream(subtitle);
+ EXPECT_EQ(1, a.GetStreamCount(CStreamDetail::SUBTITLE));
+ EXPECT_EQ(1, a.GetSubtitleStreamCount());
+
+ a.DetermineBestStreams();
+ EXPECT_STREQ("h264", a.GetVideoCodec().c_str());
+ EXPECT_EQ(2.39f, a.GetVideoAspect());
+ EXPECT_EQ(1920, a.GetVideoWidth());
+ EXPECT_EQ(1080, a.GetVideoHeight());
+ EXPECT_EQ(30, a.GetVideoDuration());
+ EXPECT_STREQ("left_right", a.GetStereoMode().c_str());
+}
+
+TEST(TestStreamDetails, VideoDimsToResolutionDescription)
+{
+ EXPECT_STREQ("1080",
+ CStreamDetails::VideoDimsToResolutionDescription(1920, 1080).c_str());
+}
+
+TEST(TestStreamDetails, VideoAspectToAspectDescription)
+{
+ EXPECT_STREQ("2.40", CStreamDetails::VideoAspectToAspectDescription(2.39f).c_str());
+}
diff --git a/xbmc/utils/test/TestStreamUtils.cpp b/xbmc/utils/test/TestStreamUtils.cpp
new file mode 100644
index 0000000..e23f958
--- /dev/null
+++ b/xbmc/utils/test/TestStreamUtils.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/StreamUtils.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestStreamUtils, General)
+{
+ EXPECT_EQ(0, StreamUtils::GetCodecPriority(""));
+ EXPECT_EQ(1, StreamUtils::GetCodecPriority("ac3"));
+ EXPECT_EQ(2, StreamUtils::GetCodecPriority("dca"));
+ EXPECT_EQ(3, StreamUtils::GetCodecPriority("eac3"));
+ EXPECT_EQ(4, StreamUtils::GetCodecPriority("dtshd_hra"));
+ EXPECT_EQ(5, StreamUtils::GetCodecPriority("dtshd_ma"));
+ EXPECT_EQ(6, StreamUtils::GetCodecPriority("truehd"));
+ EXPECT_EQ(7, StreamUtils::GetCodecPriority("flac"));
+}
diff --git a/xbmc/utils/test/TestStringUtils.cpp b/xbmc/utils/test/TestStringUtils.cpp
new file mode 100644
index 0000000..82a78b1
--- /dev/null
+++ b/xbmc/utils/test/TestStringUtils.cpp
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/StringUtils.h"
+
+#include <algorithm>
+
+#include <gtest/gtest.h>
+enum class ECG
+{
+ A,
+ B
+};
+
+enum EG
+{
+ C,
+ D
+};
+
+namespace test_enum
+{
+enum class ECN
+{
+ A = 1,
+ B
+};
+enum EN
+{
+ C = 1,
+ D
+};
+}
+TEST(TestStringUtils, Format)
+{
+ std::string refstr = "test 25 2.7 ff FF";
+
+ std::string varstr =
+ StringUtils::Format("{} {} {:.1f} {:x} {:02X}", "test", 25, 2.743f, 0x00ff, 0x00ff);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = StringUtils::Format("", "test", 25, 2.743f, 0x00ff, 0x00ff);
+ EXPECT_STREQ("", varstr.c_str());
+}
+
+TEST(TestStringUtils, FormatEnum)
+{
+ const char* zero = "0";
+ const char* one = "1";
+
+ std::string varstr = StringUtils::Format("{}", ECG::A);
+ EXPECT_STREQ(zero, varstr.c_str());
+
+ varstr = StringUtils::Format("{}", EG::C);
+ EXPECT_STREQ(zero, varstr.c_str());
+
+ varstr = StringUtils::Format("{}", test_enum::ECN::A);
+ EXPECT_STREQ(one, varstr.c_str());
+
+ varstr = StringUtils::Format("{}", test_enum::EN::C);
+ EXPECT_STREQ(one, varstr.c_str());
+}
+
+TEST(TestStringUtils, FormatEnumWidth)
+{
+ const char* one = "01";
+
+ std::string varstr = StringUtils::Format("{:02d}", ECG::B);
+ EXPECT_STREQ(one, varstr.c_str());
+
+ varstr = StringUtils::Format("{:02}", EG::D);
+ EXPECT_STREQ(one, varstr.c_str());
+}
+
+TEST(TestStringUtils, ToUpper)
+{
+ std::string refstr = "TEST";
+
+ std::string varstr = "TeSt";
+ StringUtils::ToUpper(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, ToLower)
+{
+ std::string refstr = "test";
+
+ std::string varstr = "TeSt";
+ StringUtils::ToLower(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, ToCapitalize)
+{
+ std::string refstr = "Test";
+ std::string varstr = "test";
+ StringUtils::ToCapitalize(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "Just A Test";
+ varstr = "just a test";
+ StringUtils::ToCapitalize(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "Test -1;2:3, String For Case";
+ varstr = "test -1;2:3, string for Case";
+ StringUtils::ToCapitalize(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = " JuST Another\t\tTEst:\nWoRKs ";
+ varstr = " juST another\t\ttEst:\nwoRKs ";
+ StringUtils::ToCapitalize(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "N.Y.P.D";
+ varstr = "n.y.p.d";
+ StringUtils::ToCapitalize(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "N-Y-P-D";
+ varstr = "n-y-p-d";
+ StringUtils::ToCapitalize(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, EqualsNoCase)
+{
+ std::string refstr = "TeSt";
+
+ EXPECT_TRUE(StringUtils::EqualsNoCase(refstr, "TeSt"));
+ EXPECT_TRUE(StringUtils::EqualsNoCase(refstr, "tEsT"));
+}
+
+TEST(TestStringUtils, Left)
+{
+ std::string refstr, varstr;
+ std::string origstr = "test";
+
+ refstr = "";
+ varstr = StringUtils::Left(origstr, 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "te";
+ varstr = StringUtils::Left(origstr, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "test";
+ varstr = StringUtils::Left(origstr, 10);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Mid)
+{
+ std::string refstr, varstr;
+ std::string origstr = "test";
+
+ refstr = "";
+ varstr = StringUtils::Mid(origstr, 0, 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "te";
+ varstr = StringUtils::Mid(origstr, 0, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "test";
+ varstr = StringUtils::Mid(origstr, 0, 10);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "st";
+ varstr = StringUtils::Mid(origstr, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "st";
+ varstr = StringUtils::Mid(origstr, 2, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "es";
+ varstr = StringUtils::Mid(origstr, 1, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Right)
+{
+ std::string refstr, varstr;
+ std::string origstr = "test";
+
+ refstr = "";
+ varstr = StringUtils::Right(origstr, 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "st";
+ varstr = StringUtils::Right(origstr, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "test";
+ varstr = StringUtils::Right(origstr, 10);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Trim)
+{
+ std::string refstr = "test test";
+
+ std::string varstr = " test test ";
+ StringUtils::Trim(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, TrimLeft)
+{
+ std::string refstr = "test test ";
+
+ std::string varstr = " test test ";
+ StringUtils::TrimLeft(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, TrimRight)
+{
+ std::string refstr = " test test";
+
+ std::string varstr = " test test ";
+ StringUtils::TrimRight(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Replace)
+{
+ std::string refstr = "text text";
+
+ std::string varstr = "test test";
+ EXPECT_EQ(StringUtils::Replace(varstr, 's', 'x'), 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ EXPECT_EQ(StringUtils::Replace(varstr, 's', 'x'), 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = "test test";
+ EXPECT_EQ(StringUtils::Replace(varstr, "s", "x"), 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ EXPECT_EQ(StringUtils::Replace(varstr, "s", "x"), 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, StartsWith)
+{
+ std::string refstr = "test";
+
+ EXPECT_FALSE(StringUtils::StartsWithNoCase(refstr, "x"));
+
+ EXPECT_TRUE(StringUtils::StartsWith(refstr, "te"));
+ EXPECT_TRUE(StringUtils::StartsWith(refstr, "test"));
+ EXPECT_FALSE(StringUtils::StartsWith(refstr, "Te"));
+
+ EXPECT_TRUE(StringUtils::StartsWithNoCase(refstr, "Te"));
+ EXPECT_TRUE(StringUtils::StartsWithNoCase(refstr, "TesT"));
+}
+
+TEST(TestStringUtils, EndsWith)
+{
+ std::string refstr = "test";
+
+ EXPECT_FALSE(StringUtils::EndsWithNoCase(refstr, "x"));
+
+ EXPECT_TRUE(StringUtils::EndsWith(refstr, "st"));
+ EXPECT_TRUE(StringUtils::EndsWith(refstr, "test"));
+ EXPECT_FALSE(StringUtils::EndsWith(refstr, "sT"));
+
+ EXPECT_TRUE(StringUtils::EndsWithNoCase(refstr, "sT"));
+ EXPECT_TRUE(StringUtils::EndsWithNoCase(refstr, "TesT"));
+}
+
+TEST(TestStringUtils, Join)
+{
+ std::string refstr, varstr;
+ std::vector<std::string> strarray;
+
+ strarray.emplace_back("a");
+ strarray.emplace_back("b");
+ strarray.emplace_back("c");
+ strarray.emplace_back("de");
+ strarray.emplace_back(",");
+ strarray.emplace_back("fg");
+ strarray.emplace_back(",");
+ refstr = "a,b,c,de,,,fg,,";
+ varstr = StringUtils::Join(strarray, ",");
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Split)
+{
+ std::vector<std::string> varresults;
+
+ // test overload with string as delimiter
+ varresults = StringUtils::Split("g,h,ij,k,lm,,n", ",");
+ EXPECT_STREQ("g", varresults.at(0).c_str());
+ EXPECT_STREQ("h", varresults.at(1).c_str());
+ EXPECT_STREQ("ij", varresults.at(2).c_str());
+ EXPECT_STREQ("k", varresults.at(3).c_str());
+ EXPECT_STREQ("lm", varresults.at(4).c_str());
+ EXPECT_STREQ("", varresults.at(5).c_str());
+ EXPECT_STREQ("n", varresults.at(6).c_str());
+
+ EXPECT_TRUE(StringUtils::Split("", "|").empty());
+
+ EXPECT_EQ(4U, StringUtils::Split("a bc d ef ghi ", " ", 4).size());
+ EXPECT_STREQ("d ef ghi ", StringUtils::Split("a bc d ef ghi ", " ", 4).at(3).c_str()) << "Last part must include rest of the input string";
+ EXPECT_EQ(7U, StringUtils::Split("a bc d ef ghi ", " ").size()) << "Result must be 7 strings including two empty strings";
+ EXPECT_STREQ("bc", StringUtils::Split("a bc d ef ghi ", " ").at(1).c_str());
+ EXPECT_STREQ("", StringUtils::Split("a bc d ef ghi ", " ").at(2).c_str());
+ EXPECT_STREQ("", StringUtils::Split("a bc d ef ghi ", " ").at(6).c_str());
+
+ EXPECT_EQ(2U, StringUtils::Split("a bc d ef ghi ", " ").size());
+ EXPECT_EQ(2U, StringUtils::Split("a bc d ef ghi ", " ", 10).size());
+ EXPECT_STREQ("a bc", StringUtils::Split("a bc d ef ghi ", " ", 10).at(0).c_str());
+
+ EXPECT_EQ(1U, StringUtils::Split("a bc d ef ghi ", " z").size());
+ EXPECT_STREQ("a bc d ef ghi ", StringUtils::Split("a bc d ef ghi ", " z").at(0).c_str());
+
+ EXPECT_EQ(1U, StringUtils::Split("a bc d ef ghi ", "").size());
+ EXPECT_STREQ("a bc d ef ghi ", StringUtils::Split("a bc d ef ghi ", "").at(0).c_str());
+
+ // test overload with char as delimiter
+ EXPECT_EQ(4U, StringUtils::Split("a bc d ef ghi ", ' ', 4).size());
+ EXPECT_STREQ("d ef ghi ", StringUtils::Split("a bc d ef ghi ", ' ', 4).at(3).c_str());
+ EXPECT_EQ(7U, StringUtils::Split("a bc d ef ghi ", ' ').size()) << "Result must be 7 strings including two empty strings";
+ EXPECT_STREQ("bc", StringUtils::Split("a bc d ef ghi ", ' ').at(1).c_str());
+ EXPECT_STREQ("", StringUtils::Split("a bc d ef ghi ", ' ').at(2).c_str());
+ EXPECT_STREQ("", StringUtils::Split("a bc d ef ghi ", ' ').at(6).c_str());
+
+ EXPECT_EQ(1U, StringUtils::Split("a bc d ef ghi ", 'z').size());
+ EXPECT_STREQ("a bc d ef ghi ", StringUtils::Split("a bc d ef ghi ", 'z').at(0).c_str());
+
+ EXPECT_EQ(1U, StringUtils::Split("a bc d ef ghi ", "").size());
+ EXPECT_STREQ("a bc d ef ghi ", StringUtils::Split("a bc d ef ghi ", 'z').at(0).c_str());
+}
+
+TEST(TestStringUtils, FindNumber)
+{
+ EXPECT_EQ(3, StringUtils::FindNumber("aabcaadeaa", "aa"));
+ EXPECT_EQ(1, StringUtils::FindNumber("aabcaadeaa", "b"));
+}
+
+TEST(TestStringUtils, AlphaNumericCompare)
+{
+ int64_t ref, var;
+
+ ref = 0;
+ var = StringUtils::AlphaNumericCompare(L"123abc", L"abc123");
+ EXPECT_LT(var, ref);
+}
+
+TEST(TestStringUtils, TimeStringToSeconds)
+{
+ EXPECT_EQ(77455, StringUtils::TimeStringToSeconds("21:30:55"));
+ EXPECT_EQ(7*60, StringUtils::TimeStringToSeconds("7 min"));
+ EXPECT_EQ(7*60, StringUtils::TimeStringToSeconds("7 min\t"));
+ EXPECT_EQ(154*60, StringUtils::TimeStringToSeconds(" 154 min"));
+ EXPECT_EQ(1*60+1, StringUtils::TimeStringToSeconds("1:01"));
+ EXPECT_EQ(4*60+3, StringUtils::TimeStringToSeconds("4:03"));
+ EXPECT_EQ(2*3600+4*60+3, StringUtils::TimeStringToSeconds("2:04:03"));
+ EXPECT_EQ(2*3600+4*60+3, StringUtils::TimeStringToSeconds(" 2:4:3"));
+ EXPECT_EQ(2*3600+4*60+3, StringUtils::TimeStringToSeconds(" \t\t 02:04:03 \n "));
+ EXPECT_EQ(1*3600+5*60+2, StringUtils::TimeStringToSeconds("01:05:02:04:03 \n "));
+ EXPECT_EQ(0, StringUtils::TimeStringToSeconds("blah"));
+ EXPECT_EQ(0, StringUtils::TimeStringToSeconds("ля-ля"));
+}
+
+TEST(TestStringUtils, RemoveCRLF)
+{
+ std::string refstr, varstr;
+
+ refstr = "test\r\nstring\nblah blah";
+ varstr = "test\r\nstring\nblah blah\n";
+ StringUtils::RemoveCRLF(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, utf8_strlen)
+{
+ size_t ref, var;
+
+ ref = 9;
+ var = StringUtils::utf8_strlen("test_UTF8");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, SecondsToTimeString)
+{
+ std::string ref, var;
+
+ ref = "21:30:55";
+ var = StringUtils::SecondsToTimeString(77455);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST(TestStringUtils, IsNaturalNumber)
+{
+ EXPECT_TRUE(StringUtils::IsNaturalNumber("10"));
+ EXPECT_TRUE(StringUtils::IsNaturalNumber(" 10"));
+ EXPECT_TRUE(StringUtils::IsNaturalNumber("0"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber(" 1 0"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("1.0"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("1.1"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("0x1"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("blah"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("120 h"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber(" "));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber(""));
+}
+
+TEST(TestStringUtils, IsInteger)
+{
+ EXPECT_TRUE(StringUtils::IsInteger("10"));
+ EXPECT_TRUE(StringUtils::IsInteger(" -10"));
+ EXPECT_TRUE(StringUtils::IsInteger("0"));
+ EXPECT_FALSE(StringUtils::IsInteger(" 1 0"));
+ EXPECT_FALSE(StringUtils::IsInteger("1.0"));
+ EXPECT_FALSE(StringUtils::IsInteger("1.1"));
+ EXPECT_FALSE(StringUtils::IsInteger("0x1"));
+ EXPECT_FALSE(StringUtils::IsInteger("blah"));
+ EXPECT_FALSE(StringUtils::IsInteger("120 h"));
+ EXPECT_FALSE(StringUtils::IsInteger(" "));
+ EXPECT_FALSE(StringUtils::IsInteger(""));
+}
+
+TEST(TestStringUtils, SizeToString)
+{
+ std::string ref, var;
+
+ ref = "2.00 GB";
+ var = StringUtils::SizeToString(2147483647);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "0.00 B";
+ var = StringUtils::SizeToString(0);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST(TestStringUtils, EmptyString)
+{
+ EXPECT_STREQ("", StringUtils::Empty.c_str());
+}
+
+TEST(TestStringUtils, FindWords)
+{
+ size_t ref, var;
+
+ ref = 5;
+ var = StringUtils::FindWords("test string", "string");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("12345string", "string");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("apple2012", "2012");
+ EXPECT_EQ(ref, var);
+ ref = -1;
+ var = StringUtils::FindWords("12345string", "ring");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("12345string", "345");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("apple2012", "e2012");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("apple2012", "12");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, FindWords_NonAscii)
+{
+ size_t ref, var;
+
+ ref = 6;
+ var = StringUtils::FindWords("我的视频", "视频");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("我的视频", "视");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("Apple ple", "ple");
+ EXPECT_EQ(ref, var);
+ ref = 7;
+ var = StringUtils::FindWords("Äpfel.pfel", "pfel");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, FindEndBracket)
+{
+ int ref, var;
+
+ ref = 11;
+ var = StringUtils::FindEndBracket("atest testbb test", 'a', 'b');
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, DateStringToYYYYMMDD)
+{
+ int ref, var;
+
+ ref = 20120706;
+ var = StringUtils::DateStringToYYYYMMDD("2012-07-06");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, WordToDigits)
+{
+ std::string ref, var;
+
+ ref = "8378 787464";
+ var = "test string";
+ StringUtils::WordToDigits(var);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST(TestStringUtils, CreateUUID)
+{
+ std::cout << "CreateUUID(): " << StringUtils::CreateUUID() << std::endl;
+}
+
+TEST(TestStringUtils, ValidateUUID)
+{
+ EXPECT_TRUE(StringUtils::ValidateUUID(StringUtils::CreateUUID()));
+}
+
+TEST(TestStringUtils, CompareFuzzy)
+{
+ double ref, var;
+
+ ref = 6.25;
+ var = StringUtils::CompareFuzzy("test string", "string test");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, FindBestMatch)
+{
+ double refdouble, vardouble;
+ int refint, varint;
+ std::vector<std::string> strarray;
+
+ refint = 3;
+ refdouble = 0.5625;
+ strarray.emplace_back("");
+ strarray.emplace_back("a");
+ strarray.emplace_back("e");
+ strarray.emplace_back("es");
+ strarray.emplace_back("t");
+ varint = StringUtils::FindBestMatch("test", strarray, vardouble);
+ EXPECT_EQ(refint, varint);
+ EXPECT_EQ(refdouble, vardouble);
+}
+
+TEST(TestStringUtils, Paramify)
+{
+ const char *input = "some, very \\ odd \"string\"";
+ const char *ref = "\"some, very \\\\ odd \\\"string\\\"\"";
+
+ std::string result = StringUtils::Paramify(input);
+ EXPECT_STREQ(ref, result.c_str());
+}
+
+TEST(TestStringUtils, sortstringbyname)
+{
+ std::vector<std::string> strarray;
+ strarray.emplace_back("B");
+ strarray.emplace_back("c");
+ strarray.emplace_back("a");
+ std::sort(strarray.begin(), strarray.end(), sortstringbyname());
+
+ EXPECT_STREQ("a", strarray[0].c_str());
+ EXPECT_STREQ("B", strarray[1].c_str());
+ EXPECT_STREQ("c", strarray[2].c_str());
+}
+
+TEST(TestStringUtils, FileSizeFormat)
+{
+ EXPECT_STREQ("0B", StringUtils::FormatFileSize(0).c_str());
+
+ EXPECT_STREQ("999B", StringUtils::FormatFileSize(999).c_str());
+ EXPECT_STREQ("0.98kB", StringUtils::FormatFileSize(1000).c_str());
+
+ EXPECT_STREQ("1.00kB", StringUtils::FormatFileSize(1024).c_str());
+ EXPECT_STREQ("9.99kB", StringUtils::FormatFileSize(10229).c_str());
+
+ EXPECT_STREQ("10.1kB", StringUtils::FormatFileSize(10387).c_str());
+ EXPECT_STREQ("99.9kB", StringUtils::FormatFileSize(102297).c_str());
+
+ EXPECT_STREQ("100kB", StringUtils::FormatFileSize(102400).c_str());
+ EXPECT_STREQ("999kB", StringUtils::FormatFileSize(1023431).c_str());
+
+ EXPECT_STREQ("0.98MB", StringUtils::FormatFileSize(1023897).c_str());
+ EXPECT_STREQ("0.98MB", StringUtils::FormatFileSize(1024000).c_str());
+
+ //Last unit should overflow the 3 digit limit
+ EXPECT_STREQ("5432PB", StringUtils::FormatFileSize(6115888293969133568).c_str());
+}
+
+TEST(TestStringUtils, ToHexadecimal)
+{
+ EXPECT_STREQ("", StringUtils::ToHexadecimal("").c_str());
+ EXPECT_STREQ("616263", StringUtils::ToHexadecimal("abc").c_str());
+ std::string a{"a\0b\n", 4};
+ EXPECT_STREQ("6100620a", StringUtils::ToHexadecimal(a).c_str());
+ std::string nul{"\0", 1};
+ EXPECT_STREQ("00", StringUtils::ToHexadecimal(nul).c_str());
+ std::string ff{"\xFF", 1};
+ EXPECT_STREQ("ff", StringUtils::ToHexadecimal(ff).c_str());
+}
diff --git a/xbmc/utils/test/TestSystemInfo.cpp b/xbmc/utils/test/TestSystemInfo.cpp
new file mode 100644
index 0000000..d14a474
--- /dev/null
+++ b/xbmc/utils/test/TestSystemInfo.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GUIInfoManager.h"
+#include "ServiceBroker.h"
+#include "settings/Settings.h"
+#include "utils/CPUInfo.h"
+#include "utils/SystemInfo.h"
+#if defined(TARGET_WINDOWS)
+#include "platform/win32/CharsetConverter.h"
+#endif
+
+#include <gtest/gtest.h>
+
+#include "PlatformDefs.h"
+
+class TestSystemInfo : public testing::Test
+{
+protected:
+ TestSystemInfo() { CServiceBroker::RegisterCPUInfo(CCPUInfo::GetCPUInfo()); }
+ ~TestSystemInfo() { CServiceBroker::UnregisterCPUInfo(); }
+};
+
+TEST_F(TestSystemInfo, Print_System_Info)
+{
+ std::cout << "'GetKernelName(false)': \"" << g_sysinfo.GetKernelName(true) << "\"\n";
+ std::cout << "'GetKernelVersion()': \"" << g_sysinfo.GetKernelVersion() << "\"\n";
+ std::cout << "'GetKernelVersionFull()': \"" << g_sysinfo.GetKernelVersionFull() << "\"\n";
+ std::cout << "'GetOsPrettyNameWithVersion()': \"" << g_sysinfo.GetOsPrettyNameWithVersion() << "\"\n";
+ std::cout << "'GetOsName(false)': \"" << g_sysinfo.GetOsName(false) << "\"\n";
+ std::cout << "'GetOsVersion()': \"" << g_sysinfo.GetOsVersion() << "\"\n";
+ std::cout << "'GetKernelCpuFamily()': \"" << g_sysinfo.GetKernelCpuFamily() << "\"\n";
+ std::cout << "'GetKernelBitness()': \"" << g_sysinfo.GetKernelBitness() << "\"\n";
+ std::cout << "'GetBuildTargetPlatformName()': \"" << g_sysinfo.GetBuildTargetPlatformName() << "\"\n";
+ std::cout << "'GetBuildTargetPlatformVersionDecoded()': \"" << g_sysinfo.GetBuildTargetPlatformVersionDecoded() << "\"\n";
+ std::cout << "'GetBuildTargetPlatformVersion()': \"" << g_sysinfo.GetBuildTargetPlatformVersion() << "\"\n";
+ std::cout << "'GetBuildTargetCpuFamily()': \"" << g_sysinfo.GetBuildTargetCpuFamily() << "\"\n";
+ std::cout << "'GetXbmcBitness()': \"" << g_sysinfo.GetXbmcBitness() << "\"\n";
+ std::cout << "'GetUsedCompilerNameAndVer()': \"" << g_sysinfo.GetUsedCompilerNameAndVer() << "\"\n";
+ std::cout << "'GetManufacturerName()': \"" << g_sysinfo.GetManufacturerName() << "\"\n";
+ std::cout << "'GetModelName()': \"" << g_sysinfo.GetModelName() << "\"\n";
+ std::cout << "'GetUserAgent()': \"" << g_sysinfo.GetUserAgent() << "\"\n";
+}
+
+TEST_F(TestSystemInfo, GetKernelName)
+{
+ EXPECT_FALSE(g_sysinfo.GetKernelName(true).empty()) << "'GetKernelName(true)' must not return empty kernel name";
+ EXPECT_FALSE(g_sysinfo.GetKernelName(false).empty()) << "'GetKernelName(false)' must not return empty kernel name";
+ EXPECT_STRCASENE("Unknown kernel", g_sysinfo.GetKernelName(true).c_str()) << "'GetKernelName(true)' must not return 'Unknown kernel'";
+ EXPECT_STRCASENE("Unknown kernel", g_sysinfo.GetKernelName(false).c_str()) << "'GetKernelName(false)' must not return 'Unknown kernel'";
+#ifndef TARGET_DARWIN
+ EXPECT_EQ(g_sysinfo.GetBuildTargetPlatformName(), g_sysinfo.GetKernelName(true)) << "'GetKernelName(true)' must match GetBuildTargetPlatformName()";
+ EXPECT_EQ(g_sysinfo.GetBuildTargetPlatformName(), g_sysinfo.GetKernelName(false)) << "'GetKernelName(false)' must match GetBuildTargetPlatformName()";
+#endif // !TARGET_DARWIN
+#if defined(TARGET_WINDOWS)
+ EXPECT_NE(std::string::npos, g_sysinfo.GetKernelName(true).find("Windows")) << "'GetKernelName(true)' must contain 'Windows'";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetKernelName(false).find("Windows")) << "'GetKernelName(false)' must contain 'Windows'";
+#elif defined(TARGET_FREEBSD)
+ EXPECT_STREQ("FreeBSD", g_sysinfo.GetKernelName(true).c_str()) << "'GetKernelName(true)' must return 'FreeBSD'";
+ EXPECT_STREQ("FreeBSD", g_sysinfo.GetKernelName(false).c_str()) << "'GetKernelName(false)' must return 'FreeBSD'";
+#elif defined(TARGET_DARWIN)
+ EXPECT_STREQ("Darwin", g_sysinfo.GetKernelName(true).c_str()) << "'GetKernelName(true)' must return 'Darwin'";
+ EXPECT_STREQ("Darwin", g_sysinfo.GetKernelName(false).c_str()) << "'GetKernelName(false)' must return 'Darwin'";
+#elif defined(TARGET_LINUX)
+ EXPECT_STREQ("Linux", g_sysinfo.GetKernelName(true).c_str()) << "'GetKernelName(true)' must return 'Linux'";
+ EXPECT_STREQ("Linux", g_sysinfo.GetKernelName(false).c_str()) << "'GetKernelName(false)' must return 'Linux'";
+#endif
+}
+
+TEST_F(TestSystemInfo, GetKernelVersionFull)
+{
+ EXPECT_FALSE(g_sysinfo.GetKernelVersionFull().empty()) << "'GetKernelVersionFull()' must not return empty string";
+ EXPECT_STRNE("0.0.0", g_sysinfo.GetKernelVersionFull().c_str()) << "'GetKernelVersionFull()' must not return '0.0.0'";
+ EXPECT_STRNE("0.0", g_sysinfo.GetKernelVersionFull().c_str()) << "'GetKernelVersionFull()' must not return '0.0'";
+ EXPECT_EQ(0U, g_sysinfo.GetKernelVersionFull().find_first_of("0123456789")) << "'GetKernelVersionFull()' must not return version not starting from digit";
+}
+
+TEST_F(TestSystemInfo, GetKernelVersion)
+{
+ EXPECT_FALSE(g_sysinfo.GetKernelVersion().empty()) << "'GetKernelVersion()' must not return empty string";
+ EXPECT_STRNE("0.0.0", g_sysinfo.GetKernelVersion().c_str()) << "'GetKernelVersion()' must not return '0.0.0'";
+ EXPECT_STRNE("0.0", g_sysinfo.GetKernelVersion().c_str()) << "'GetKernelVersion()' must not return '0.0'";
+ EXPECT_EQ(0U, g_sysinfo.GetKernelVersion().find_first_of("0123456789")) << "'GetKernelVersion()' must not return version not starting from digit";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetKernelVersion().find_first_not_of("0123456789.")) << "'GetKernelVersion()' must not return version with not only digits and dots";
+}
+
+TEST_F(TestSystemInfo, GetOsName)
+{
+ EXPECT_FALSE(g_sysinfo.GetOsName(true).empty()) << "'GetOsName(true)' must not return empty OS name";
+ EXPECT_FALSE(g_sysinfo.GetOsName(false).empty()) << "'GetOsName(false)' must not return empty OS name";
+ EXPECT_STRCASENE("Unknown OS", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must not return 'Unknown OS'";
+ EXPECT_STRCASENE("Unknown OS", g_sysinfo.GetOsName(false).c_str()) << "'GetOsName(false)' must not return 'Unknown OS'";
+#if defined(TARGET_WINDOWS)
+ EXPECT_NE(std::string::npos, g_sysinfo.GetOsName(true).find("Windows")) << "'GetOsName(true)' must contain 'Windows'";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetOsName(false).find("Windows")) << "'GetOsName(false)' must contain 'Windows'";
+#elif defined(TARGET_FREEBSD)
+ EXPECT_STREQ("FreeBSD", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must return 'FreeBSD'";
+ EXPECT_STREQ("FreeBSD", g_sysinfo.GetOsName(false).c_str()) << "'GetOsName(false)' must return 'FreeBSD'";
+#elif defined(TARGET_DARWIN_IOS)
+ EXPECT_STREQ("iOS", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must return 'iOS'";
+ EXPECT_STREQ("iOS", g_sysinfo.GetOsName(false).c_str()) << "'GetOsName(false)' must return 'iOS'";
+#elif defined(TARGET_DARWIN_TVOS)
+ EXPECT_STREQ("tvOS", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must return 'tvOS'";
+ EXPECT_STREQ("tvOS", g_sysinfo.GetOsName(false).c_str())
+ << "'GetOsName(false)' must return 'tvOS'";
+#elif defined(TARGET_DARWIN_OSX)
+ EXPECT_STREQ("macOS", g_sysinfo.GetOsName(true).c_str())
+ << "'GetOsName(true)' must return 'macOS'";
+ EXPECT_STREQ("macOS", g_sysinfo.GetOsName(false).c_str())
+ << "'GetOsName(false)' must return 'macOS'";
+#elif defined(TARGET_ANDROID)
+ EXPECT_STREQ("Android", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must return 'Android'";
+ EXPECT_STREQ("Android", g_sysinfo.GetOsName(false).c_str()) << "'GetOsName(false)' must return 'Android'";
+#endif
+#ifdef TARGET_DARWIN
+ EXPECT_EQ(g_sysinfo.GetBuildTargetPlatformName(), g_sysinfo.GetOsName(true)) << "'GetOsName(true)' must match GetBuildTargetPlatformName()";
+ EXPECT_EQ(g_sysinfo.GetBuildTargetPlatformName(), g_sysinfo.GetOsName(false)) << "'GetOsName(false)' must match GetBuildTargetPlatformName()";
+#endif // TARGET_DARWIN
+}
+
+TEST_F(TestSystemInfo, DISABLED_GetOsVersion)
+{
+ EXPECT_FALSE(g_sysinfo.GetOsVersion().empty()) << "'GetOsVersion()' must not return empty string";
+ EXPECT_STRNE("0.0.0", g_sysinfo.GetOsVersion().c_str()) << "'GetOsVersion()' must not return '0.0.0'";
+ EXPECT_STRNE("0.0", g_sysinfo.GetOsVersion().c_str()) << "'GetOsVersion()' must not return '0.0'";
+ EXPECT_EQ(0U, g_sysinfo.GetOsVersion().find_first_of("0123456789")) << "'GetOsVersion()' must not return version not starting from digit";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetOsVersion().find_first_not_of("0123456789.")) << "'GetOsVersion()' must not return version with not only digits and dots";
+}
+
+TEST_F(TestSystemInfo, GetOsPrettyNameWithVersion)
+{
+ EXPECT_FALSE(g_sysinfo.GetOsPrettyNameWithVersion().empty()) << "'GetOsPrettyNameWithVersion()' must not return empty string";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetOsPrettyNameWithVersion().find("Unknown")) << "'GetOsPrettyNameWithVersion()' must not contain 'Unknown'";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetOsPrettyNameWithVersion().find("unknown")) << "'GetOsPrettyNameWithVersion()' must not contain 'unknown'";
+#ifdef TARGET_WINDOWS
+ EXPECT_NE(std::string::npos, g_sysinfo.GetOsPrettyNameWithVersion().find("Windows")) << "'GetOsPrettyNameWithVersion()' must contain 'Windows'";
+#else // ! TARGET_WINDOWS
+ EXPECT_NE(std::string::npos, g_sysinfo.GetOsPrettyNameWithVersion().find(g_sysinfo.GetOsVersion())) << "'GetOsPrettyNameWithVersion()' must contain OS version";
+#endif // ! TARGET_WINDOWS
+}
+
+TEST_F(TestSystemInfo, GetManufacturerName)
+{
+ EXPECT_STRCASENE("unknown", g_sysinfo.GetManufacturerName().c_str()) << "'GetManufacturerName()' must return empty string instead of 'Unknown'";
+}
+
+TEST_F(TestSystemInfo, GetModelName)
+{
+ EXPECT_STRCASENE("unknown", g_sysinfo.GetModelName().c_str()) << "'GetModelName()' must return empty string instead of 'Unknown'";
+}
+
+#ifndef TARGET_WINDOWS
+TEST_F(TestSystemInfo, IsAeroDisabled)
+{
+ EXPECT_FALSE(g_sysinfo.IsAeroDisabled()) << "'IsAeroDisabled()' must return 'false'";
+}
+#endif // ! TARGET_WINDOWS
+
+TEST_F(TestSystemInfo, IsWindowsVersion)
+{
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersion(CSysInfo::WindowsVersionUnknown)) << "'IsWindowsVersion()' must return 'false' for 'WindowsVersionUnknown'";
+#ifndef TARGET_WINDOWS
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersion(CSysInfo::WindowsVersionWin7)) << "'IsWindowsVersion()' must return 'false'";
+#endif // ! TARGET_WINDOWS
+}
+
+TEST_F(TestSystemInfo, IsWindowsVersionAtLeast)
+{
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersionAtLeast(CSysInfo::WindowsVersionUnknown)) << "'IsWindowsVersionAtLeast()' must return 'false' for 'WindowsVersionUnknown'";
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersionAtLeast(CSysInfo::WindowsVersionFuture)) << "'IsWindowsVersionAtLeast()' must return 'false' for 'WindowsVersionFuture'";
+#ifndef TARGET_WINDOWS
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersion(CSysInfo::WindowsVersionWin7)) << "'IsWindowsVersionAtLeast()' must return 'false'";
+#endif // ! TARGET_WINDOWS
+}
+
+TEST_F(TestSystemInfo, GetWindowsVersion)
+{
+#ifdef TARGET_WINDOWS
+ EXPECT_NE(CSysInfo::WindowsVersionUnknown, g_sysinfo.GetWindowsVersion()) << "'GetWindowsVersion()' must not return 'WindowsVersionUnknown'";
+ EXPECT_NE(CSysInfo::WindowsVersionFuture, g_sysinfo.GetWindowsVersion()) << "'GetWindowsVersion()' must not return 'WindowsVersionFuture'";
+#else // ! TARGET_WINDOWS
+ EXPECT_EQ(CSysInfo::WindowsVersionUnknown, g_sysinfo.GetWindowsVersion()) << "'GetWindowsVersion()' must return 'WindowsVersionUnknown'";
+#endif // ! TARGET_WINDOWS
+}
+
+TEST_F(TestSystemInfo, GetKernelBitness)
+{
+ EXPECT_TRUE(g_sysinfo.GetKernelBitness() == 32 || g_sysinfo.GetKernelBitness() == 64) << "'GetKernelBitness()' must return '32' or '64', but not '" << g_sysinfo.GetKernelBitness() << "'";
+ EXPECT_LE(g_sysinfo.GetXbmcBitness(), g_sysinfo.GetKernelBitness()) << "'GetKernelBitness()' must be greater or equal to 'GetXbmcBitness()'";
+}
+
+TEST_F(TestSystemInfo, GetKernelCpuFamily)
+{
+ EXPECT_STRNE("unknown CPU family", g_sysinfo.GetKernelCpuFamily().c_str()) << "'GetKernelCpuFamily()' must not return 'unknown CPU family'";
+#if defined(__thumb__) || defined(_M_ARMT) || defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
+ EXPECT_STREQ("ARM", g_sysinfo.GetKernelCpuFamily().c_str()) << "'GetKernelCpuFamily()' must return 'ARM'";
+#else // ! ARM
+ EXPECT_EQ(g_sysinfo.GetBuildTargetCpuFamily(), g_sysinfo.GetKernelCpuFamily()) << "'GetKernelCpuFamily()' must match 'GetBuildTargetCpuFamily()'";
+#endif // ! ARM
+}
+
+TEST_F(TestSystemInfo, GetXbmcBitness)
+{
+ EXPECT_TRUE(g_sysinfo.GetXbmcBitness() == 32 || g_sysinfo.GetXbmcBitness() == 64) << "'GetXbmcBitness()' must return '32' or '64', but not '" << g_sysinfo.GetXbmcBitness() << "'";
+ EXPECT_GE(g_sysinfo.GetKernelBitness(), g_sysinfo.GetXbmcBitness()) << "'GetXbmcBitness()' must be not greater than 'GetKernelBitness()'";
+}
+
+TEST_F(TestSystemInfo, GetUserAgent)
+{
+ EXPECT_STREQ(g_sysinfo.GetAppName().c_str(), g_sysinfo.GetUserAgent().substr(0, g_sysinfo.GetAppName().size()).c_str()) << "'GetUserAgent()' string must start with app name'";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find('(')) << "'GetUserAgent()' must contain brackets around second parameter";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find(')')) << "'GetUserAgent()' must contain brackets around second parameter";
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find(' '), g_sysinfo.GetUserAgent().find(" (")) << "Second parameter in 'GetUserAgent()' string must be in brackets";
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find(" (") + 1, g_sysinfo.GetUserAgent().find('(')) << "'GetUserAgent()' string must not contain any opening brackets before second parameter";
+ EXPECT_GT(g_sysinfo.GetUserAgent().find(')'), g_sysinfo.GetUserAgent().find('(')) << "'GetUserAgent()' string must not contain any closing brackets before second parameter";
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find(") "), g_sysinfo.GetUserAgent().find(')')) << "'GetUserAgent()' string must not contain any closing brackets before end of second parameter";
+#if defined(TARGET_WINDOWS)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(Windows")) << "Second parameter in 'GetUserAgent()' string must start from `Windows`";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find("Windows")) << "'GetUserAgent()' must contain 'Windows'";
+#elif defined(TARGET_DARWIN_IOS)
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find("like Mac OS X")) << "'GetUserAgent()' must contain ' like Mac OS X'";
+ EXPECT_TRUE(g_sysinfo.GetUserAgent().find("CPU OS ") != std::string::npos || g_sysinfo.GetUserAgent().find("CPU iPhone OS ") != std::string::npos) << "'GetUserAgent()' must contain 'CPU OS ' or 'CPU iPhone OS '";
+#elif defined(TARGET_DARWIN_TVOS)
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find("like Mac OS X"))
+ << "'GetUserAgent()' must contain ' like Mac OS X'";
+ EXPECT_TRUE(g_sysinfo.GetUserAgent().find("CPU TVOS ") != std::string::npos)
+ << "'GetUserAgent()' must contain 'CPU TVOS '";
+#elif defined(TARGET_DARWIN_OSX)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(Macintosh; ")) << "Second parameter in 'GetUserAgent()' string must start from 'Macintosh; '";
+#elif defined(TARGET_ANDROID)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(Linux; Android ")) << "Second parameter in 'GetUserAgent()' string must start from 'Linux; Android '";
+#elif defined(TARGET_POSIX)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(X11; ")) << "Second parameter in 'GetUserAgent()' string must start from 'X11; '";
+#if defined(TARGET_FREEBSD)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(X11; FreeBSD ")) << "Second parameter in 'GetUserAgent()' string must start from 'X11; FreeBSD '";
+#elif defined(TARGET_LINUX)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(X11; Linux ")) << "Second parameter in 'GetUserAgent()' string must start from 'X11; Linux '";
+#endif // defined(TARGET_LINUX)
+#endif // defined(TARGET_POSIX)
+
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find(" App_Bitness/")) << "'GetUserAgent()' must contain ' App_Bitness/'";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find(" Version/")) << "'GetUserAgent()' must contain ' Version/'";
+}
+
+TEST_F(TestSystemInfo, GetBuildTargetPlatformName)
+{
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformName().find("Unknown")) << "'GetBuildTargetPlatformName()' must not contain 'Unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformName() << "'";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformName().find("unknown")) << "'GetBuildTargetPlatformName()' must not contain 'unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformName() << "'";
+}
+
+TEST_F(TestSystemInfo, GetBuildTargetPlatformVersion)
+{
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformVersion().find("Unknown")) << "'GetBuildTargetPlatformVersion()' must not contain 'Unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformVersion() << "'";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformVersion().find("unknown")) << "'GetBuildTargetPlatformVersion()' must not contain 'unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformVersion() << "'";
+}
+
+TEST_F(TestSystemInfo, GetBuildTargetPlatformVersionDecoded)
+{
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformVersionDecoded().find("Unknown")) << "'GetBuildTargetPlatformVersionDecoded()' must not contain 'Unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformVersion() << "'";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformVersionDecoded().find("unknown")) << "'GetBuildTargetPlatformVersionDecoded()' must not contain 'unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformVersion() << "'";
+#ifdef TARGET_ANDROID
+ EXPECT_STREQ("API level ", g_sysinfo.GetBuildTargetPlatformVersionDecoded().substr(0, 10).c_str()) << "'GetBuildTargetPlatformVersionDecoded()' must start from 'API level '";
+#else
+ EXPECT_STREQ("version ", g_sysinfo.GetBuildTargetPlatformVersionDecoded().substr(0, 8).c_str()) << "'GetBuildTargetPlatformVersionDecoded()' must start from 'version'";
+#endif
+}
+
+TEST_F(TestSystemInfo, GetBuildTargetCpuFamily)
+{
+ EXPECT_STRNE("unknown CPU family", g_sysinfo.GetBuildTargetCpuFamily().c_str()) << "'GetBuildTargetCpuFamily()' must not return 'unknown CPU family'";
+#if defined(__thumb__) || defined(_M_ARMT) || defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
+ EXPECT_STREQ("ARM", g_sysinfo.GetBuildTargetCpuFamily().substr(0, 3).c_str()) << "'GetKernelCpuFamily()' string must start from 'ARM'";
+#else // ! ARM
+ EXPECT_EQ(g_sysinfo.GetKernelCpuFamily(), g_sysinfo.GetBuildTargetCpuFamily()) << "'GetBuildTargetCpuFamily()' must match 'GetKernelCpuFamily()'";
+#endif // ! ARM
+}
+
+TEST_F(TestSystemInfo, GetUsedCompilerNameAndVer)
+{
+ EXPECT_STRNE("unknown compiler", g_sysinfo.GetUsedCompilerNameAndVer().c_str()) << "'GetUsedCompilerNameAndVer()' must not return 'unknown compiler'";
+}
+
+TEST_F(TestSystemInfo, GetDiskSpace)
+{
+ int iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed;
+
+ iTotal = iTotalFree = iTotalUsed = iPercentFree = iPercentUsed = 0;
+ EXPECT_TRUE(g_sysinfo.GetDiskSpace("*", iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed)) << "'GetDiskSpace()' return 'false' for disk '*'";
+ EXPECT_NE(0, iTotal) << "'GetDiskSpace()' return zero total space for disk '*'";
+ EXPECT_EQ(iTotal, iTotalFree + iTotalUsed) << "'GetDiskSpace()' return 'TotalFree + TotalUsed' not equal to 'Total' for disk '*'";
+ EXPECT_EQ(100, iPercentFree + iPercentUsed) << "'GetDiskSpace()' return 'PercentFree + PercentUsed' not equal to '100' for disk '*'";
+
+ iTotal = iTotalFree = iTotalUsed = iPercentFree = iPercentUsed = 0;
+ EXPECT_TRUE(g_sysinfo.GetDiskSpace("", iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed)) << "'GetDiskSpace()' return 'false' for disk ''";
+ EXPECT_NE(0, iTotal) << "'GetDiskSpace()' return zero total space for disk ''";
+ EXPECT_EQ(iTotal, iTotalFree + iTotalUsed) << "'GetDiskSpace()' return 'TotalFree + TotalUsed' not equal to 'Total' for disk ''";
+ EXPECT_EQ(100, iPercentFree + iPercentUsed) << "'GetDiskSpace()' return 'PercentFree + PercentUsed' not equal to '100' for disk ''";
+
+#ifdef TARGET_WINDOWS
+ using KODI::PLATFORM::WINDOWS::FromW;
+ wchar_t sysDrive[300];
+ DWORD res = GetEnvironmentVariableW(L"SystemDrive", sysDrive, sizeof(sysDrive) / sizeof(wchar_t));
+ std::string sysDriveLtr;
+ if (res != 0 && res <= sizeof(sysDrive) / sizeof(wchar_t))
+ sysDriveLtr.assign(FromW(sysDrive), 0, 1);
+ else
+ sysDriveLtr = "C"; // fallback
+
+ iTotal = iTotalFree = iTotalUsed = iPercentFree = iPercentUsed = 0;
+ EXPECT_TRUE(g_sysinfo.GetDiskSpace(sysDriveLtr, iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed)) << "'GetDiskSpace()' return 'false' for disk '" << sysDriveLtr << ":'";
+ EXPECT_NE(0, iTotal) << "'GetDiskSpace()' return zero total space for disk '" << sysDriveLtr << ":'";
+ EXPECT_EQ(iTotal, iTotalFree + iTotalUsed) << "'GetDiskSpace()' return 'TotalFree + TotalUsed' not equal to 'Total' for disk '" << sysDriveLtr << ":'";
+ EXPECT_EQ(100, iPercentFree + iPercentUsed) << "'GetDiskSpace()' return 'PercentFree + PercentUsed' not equal to '100' for disk '" << sysDriveLtr << ":'";
+#elif defined(TARGET_POSIX)
+ iTotal = iTotalFree = iTotalUsed = iPercentFree = iPercentUsed = 0;
+ EXPECT_TRUE(g_sysinfo.GetDiskSpace("/", iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed)) << "'GetDiskSpace()' return 'false' for directory '/'";
+ EXPECT_NE(0, iTotal) << "'GetDiskSpace()' return zero total space for directory '/'";
+ EXPECT_EQ(iTotal, iTotalFree + iTotalUsed) << "'GetDiskSpace()' return 'TotalFree + TotalUsed' not equal to 'Total' for directory '/'";
+ EXPECT_EQ(100, iPercentFree + iPercentUsed) << "'GetDiskSpace()' return 'PercentFree + PercentUsed' not equal to '100' for directory '/'";
+#endif
+}
diff --git a/xbmc/utils/test/TestURIUtils.cpp b/xbmc/utils/test/TestURIUtils.cpp
new file mode 100644
index 0000000..7122fe9
--- /dev/null
+++ b/xbmc/utils/test/TestURIUtils.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "filesystem/MultiPathDirectory.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/URIUtils.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+using namespace XFILE;
+
+class TestURIUtils : public testing::Test
+{
+protected:
+ TestURIUtils() = default;
+ ~TestURIUtils() override
+ {
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.clear();
+ }
+};
+
+TEST_F(TestURIUtils, PathHasParent)
+{
+ EXPECT_TRUE(URIUtils::PathHasParent("/path/to/movie.avi", "/path/to/"));
+ EXPECT_FALSE(URIUtils::PathHasParent("/path/to/movie.avi", "/path/2/"));
+}
+
+TEST_F(TestURIUtils, GetDirectory)
+{
+ EXPECT_STREQ("/path/to/", URIUtils::GetDirectory("/path/to/movie.avi").c_str());
+ EXPECT_STREQ("/path/to/", URIUtils::GetDirectory("/path/to/").c_str());
+ EXPECT_STREQ("/path/to/|option=foo", URIUtils::GetDirectory("/path/to/movie.avi|option=foo").c_str());
+ EXPECT_STREQ("/path/to/|option=foo", URIUtils::GetDirectory("/path/to/|option=foo").c_str());
+ EXPECT_STREQ("", URIUtils::GetDirectory("movie.avi").c_str());
+ EXPECT_STREQ("", URIUtils::GetDirectory("movie.avi|option=foo").c_str());
+ EXPECT_STREQ("", URIUtils::GetDirectory("").c_str());
+
+ // Make sure it works when assigning to the same str as the reference parameter
+ std::string var = "/path/to/movie.avi|option=foo";
+ var = URIUtils::GetDirectory(var);
+ EXPECT_STREQ("/path/to/|option=foo", var.c_str());
+}
+
+TEST_F(TestURIUtils, GetExtension)
+{
+ EXPECT_STREQ(".avi",
+ URIUtils::GetExtension("/path/to/movie.avi").c_str());
+}
+
+TEST_F(TestURIUtils, HasExtension)
+{
+ EXPECT_TRUE (URIUtils::HasExtension("/path/to/movie.AvI"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/to/movie"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/.to/movie"));
+ EXPECT_FALSE(URIUtils::HasExtension(""));
+
+ EXPECT_TRUE (URIUtils::HasExtension("/path/to/movie.AvI", ".avi"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/to/movie.AvI", ".mkv"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/.avi/movie", ".avi"));
+ EXPECT_FALSE(URIUtils::HasExtension("", ".avi"));
+
+ EXPECT_TRUE (URIUtils::HasExtension("/path/movie.AvI", ".avi|.mkv|.mp4"));
+ EXPECT_TRUE (URIUtils::HasExtension("/path/movie.AvI", ".mkv|.avi|.mp4"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/movie.AvI", ".mpg|.mkv|.mp4"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path.mkv/movie.AvI", ".mpg|.mkv|.mp4"));
+ EXPECT_FALSE(URIUtils::HasExtension("", ".avi|.mkv|.mp4"));
+}
+
+TEST_F(TestURIUtils, GetFileName)
+{
+ EXPECT_STREQ("movie.avi",
+ URIUtils::GetFileName("/path/to/movie.avi").c_str());
+}
+
+TEST_F(TestURIUtils, RemoveExtension)
+{
+ std::string ref, var;
+
+ /* NOTE: CSettings need to be set to find other extensions. */
+ ref = "/path/to/file";
+ var = "/path/to/file.xml";
+ URIUtils::RemoveExtension(var);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, ReplaceExtension)
+{
+ std::string ref, var;
+
+ ref = "/path/to/file.xsd";
+ var = URIUtils::ReplaceExtension("/path/to/file.xml", ".xsd");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, Split)
+{
+ std::string refpath, reffile, varpath, varfile;
+
+ refpath = "/path/to/";
+ reffile = "movie.avi";
+ URIUtils::Split("/path/to/movie.avi", varpath, varfile);
+ EXPECT_STREQ(refpath.c_str(), varpath.c_str());
+ EXPECT_STREQ(reffile.c_str(), varfile.c_str());
+
+ std::string varpathOptional, varfileOptional;
+
+ refpath = "/path/to/";
+ reffile = "movie?movie.avi";
+ URIUtils::Split("/path/to/movie?movie.avi", varpathOptional, varfileOptional);
+ EXPECT_STREQ(refpath.c_str(), varpathOptional.c_str());
+ EXPECT_STREQ(reffile.c_str(), varfileOptional.c_str());
+
+ refpath = "file:///path/to/";
+ reffile = "movie.avi";
+ URIUtils::Split("file:///path/to/movie.avi?showinfo=true", varpathOptional, varfileOptional);
+ EXPECT_STREQ(refpath.c_str(), varpathOptional.c_str());
+ EXPECT_STREQ(reffile.c_str(), varfileOptional.c_str());
+}
+
+TEST_F(TestURIUtils, SplitPath)
+{
+ std::vector<std::string> strarray;
+
+ strarray = URIUtils::SplitPath("http://www.test.com/path/to/movie.avi");
+
+ EXPECT_STREQ("http://www.test.com/", strarray.at(0).c_str());
+ EXPECT_STREQ("path", strarray.at(1).c_str());
+ EXPECT_STREQ("to", strarray.at(2).c_str());
+ EXPECT_STREQ("movie.avi", strarray.at(3).c_str());
+}
+
+TEST_F(TestURIUtils, SplitPathLocal)
+{
+#ifndef TARGET_LINUX
+ const char *path = "C:\\path\\to\\movie.avi";
+#else
+ const char *path = "/path/to/movie.avi";
+#endif
+ std::vector<std::string> strarray;
+
+ strarray = URIUtils::SplitPath(path);
+
+#ifndef TARGET_LINUX
+ EXPECT_STREQ("C:", strarray.at(0).c_str());
+#else
+ EXPECT_STREQ("", strarray.at(0).c_str());
+#endif
+ EXPECT_STREQ("path", strarray.at(1).c_str());
+ EXPECT_STREQ("to", strarray.at(2).c_str());
+ EXPECT_STREQ("movie.avi", strarray.at(3).c_str());
+}
+
+TEST_F(TestURIUtils, GetCommonPath)
+{
+ std::string ref, var;
+
+ ref = "/path/";
+ var = "/path/2/movie.avi";
+ URIUtils::GetCommonPath(var, "/path/to/movie.avi");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, GetParentPath)
+{
+ std::string ref, var;
+
+ ref = "/path/to/";
+ var = URIUtils::GetParentPath("/path/to/movie.avi");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ var.clear();
+ EXPECT_TRUE(URIUtils::GetParentPath("/path/to/movie.avi", var));
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, SubstitutePath)
+{
+ std::string from, to, ref, var;
+
+ from = "C:\\My Videos";
+ to = "https://myserver/some%20other%20path";
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.push_back(std::make_pair(from, to));
+
+ from = "/this/path1";
+ to = "/some/other/path2";
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.push_back(std::make_pair(from, to));
+
+ from = "davs://otherserver/my%20music%20path";
+ to = "D:\\Local Music\\MP3 Collection";
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.push_back(std::make_pair(from, to));
+
+ ref = "https://myserver/some%20other%20path/sub%20dir/movie%20name.avi";
+ var = URIUtils::SubstitutePath("C:\\My Videos\\sub dir\\movie name.avi");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "C:\\My Videos\\sub dir\\movie name.avi";
+ var = URIUtils::SubstitutePath("https://myserver/some%20other%20path/sub%20dir/movie%20name.avi", true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "D:\\Local Music\\MP3 Collection\\Phil Collins\\Some CD\\01 - Two Hearts.mp3";
+ var = URIUtils::SubstitutePath("davs://otherserver/my%20music%20path/Phil%20Collins/Some%20CD/01%20-%20Two%20Hearts.mp3");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "davs://otherserver/my%20music%20path/Phil%20Collins/Some%20CD/01%20-%20Two%20Hearts.mp3";
+ var = URIUtils::SubstitutePath("D:\\Local Music\\MP3 Collection\\Phil Collins\\Some CD\\01 - Two Hearts.mp3", true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "/some/other/path2/to/movie.avi";
+ var = URIUtils::SubstitutePath("/this/path1/to/movie.avi");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "/this/path1/to/movie.avi";
+ var = URIUtils::SubstitutePath("/some/other/path2/to/movie.avi", true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "/no/translation path/";
+ var = URIUtils::SubstitutePath(ref);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "/no/translation path/";
+ var = URIUtils::SubstitutePath(ref, true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "c:\\no\\translation path";
+ var = URIUtils::SubstitutePath(ref);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "c:\\no\\translation path";
+ var = URIUtils::SubstitutePath(ref, true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, IsAddonsPath)
+{
+ EXPECT_TRUE(URIUtils::IsAddonsPath("addons://path/to/addons"));
+}
+
+TEST_F(TestURIUtils, IsSourcesPath)
+{
+ EXPECT_TRUE(URIUtils::IsSourcesPath("sources://path/to/sources"));
+}
+
+TEST_F(TestURIUtils, IsCDDA)
+{
+ EXPECT_TRUE(URIUtils::IsCDDA("cdda://path/to/cdda"));
+}
+
+TEST_F(TestURIUtils, IsDOSPath)
+{
+ EXPECT_TRUE(URIUtils::IsDOSPath("C://path/to/dosfile"));
+}
+
+TEST_F(TestURIUtils, IsDVD)
+{
+ EXPECT_TRUE(URIUtils::IsDVD("dvd://path/in/video_ts.ifo"));
+#if defined(TARGET_WINDOWS)
+ EXPECT_TRUE(URIUtils::IsDVD("dvd://path/in/file"));
+#else
+ EXPECT_TRUE(URIUtils::IsDVD("iso9660://path/in/video_ts.ifo"));
+ EXPECT_TRUE(URIUtils::IsDVD("udf://path/in/video_ts.ifo"));
+ EXPECT_TRUE(URIUtils::IsDVD("dvd://1"));
+#endif
+}
+
+TEST_F(TestURIUtils, IsFTP)
+{
+ EXPECT_TRUE(URIUtils::IsFTP("ftp://path/in/ftp"));
+}
+
+TEST_F(TestURIUtils, IsHD)
+{
+ EXPECT_TRUE(URIUtils::IsHD("/path/to/file"));
+ EXPECT_TRUE(URIUtils::IsHD("file:///path/to/file"));
+ EXPECT_TRUE(URIUtils::IsHD("special://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsHD("stack://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsHD("zip://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsInArchive)
+{
+ EXPECT_TRUE(URIUtils::IsInArchive("zip://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsInRAR)
+{
+ EXPECT_TRUE(URIUtils::IsInRAR("rar://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsInternetStream)
+{
+ CURL url1("http://path/to/file");
+ CURL url2("https://path/to/file");
+ EXPECT_TRUE(URIUtils::IsInternetStream(url1));
+ EXPECT_TRUE(URIUtils::IsInternetStream(url2));
+}
+
+TEST_F(TestURIUtils, IsInZIP)
+{
+ EXPECT_TRUE(URIUtils::IsInZIP("zip://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsISO9660)
+{
+ EXPECT_TRUE(URIUtils::IsISO9660("iso9660://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsLiveTV)
+{
+ EXPECT_TRUE(URIUtils::IsLiveTV("whatever://path/to/file.pvr"));
+}
+
+TEST_F(TestURIUtils, IsMultiPath)
+{
+ EXPECT_TRUE(URIUtils::IsMultiPath("multipath://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsMusicDb)
+{
+ EXPECT_TRUE(URIUtils::IsMusicDb("musicdb://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsNfs)
+{
+ EXPECT_TRUE(URIUtils::IsNfs("nfs://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsNfs("stack://nfs://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsOnDVD)
+{
+ EXPECT_TRUE(URIUtils::IsOnDVD("dvd://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnDVD("udf://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnDVD("iso9660://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnDVD("cdda://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsOnLAN)
+{
+ std::vector<std::string> multiVec;
+ multiVec.emplace_back("smb://path/to/file");
+ EXPECT_TRUE(URIUtils::IsOnLAN(CMultiPathDirectory::ConstructMultiPath(multiVec)));
+ EXPECT_TRUE(URIUtils::IsOnLAN("stack://smb://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnLAN("smb://path/to/file"));
+ EXPECT_FALSE(URIUtils::IsOnLAN("plugin://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnLAN("upnp://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsPlugin)
+{
+ EXPECT_TRUE(URIUtils::IsPlugin("plugin://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsScript)
+{
+ EXPECT_TRUE(URIUtils::IsScript("script://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsRAR)
+{
+ EXPECT_TRUE(URIUtils::IsRAR("/path/to/rarfile.rar"));
+ EXPECT_TRUE(URIUtils::IsRAR("/path/to/rarfile.cbr"));
+ EXPECT_FALSE(URIUtils::IsRAR("/path/to/file"));
+ EXPECT_FALSE(URIUtils::IsRAR("rar://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsRemote)
+{
+ EXPECT_TRUE(URIUtils::IsRemote("http://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsRemote("https://path/to/file"));
+ EXPECT_FALSE(URIUtils::IsRemote("addons://user/"));
+ EXPECT_FALSE(URIUtils::IsRemote("sources://video/"));
+ EXPECT_FALSE(URIUtils::IsRemote("videodb://movies/titles"));
+ EXPECT_FALSE(URIUtils::IsRemote("musicdb://genres/"));
+ EXPECT_FALSE(URIUtils::IsRemote("library://video/"));
+ EXPECT_FALSE(URIUtils::IsRemote("androidapp://app"));
+ EXPECT_FALSE(URIUtils::IsRemote("plugin://plugin.video.id"));
+}
+
+TEST_F(TestURIUtils, IsSmb)
+{
+ EXPECT_TRUE(URIUtils::IsSmb("smb://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsSmb("stack://smb://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsSpecial)
+{
+ EXPECT_TRUE(URIUtils::IsSpecial("special://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsSpecial("stack://special://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsStack)
+{
+ EXPECT_TRUE(URIUtils::IsStack("stack://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsUPnP)
+{
+ EXPECT_TRUE(URIUtils::IsUPnP("upnp://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsURL)
+{
+ EXPECT_TRUE(URIUtils::IsURL("someprotocol://path/to/file"));
+ EXPECT_FALSE(URIUtils::IsURL("/path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsVideoDb)
+{
+ EXPECT_TRUE(URIUtils::IsVideoDb("videodb://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsZIP)
+{
+ EXPECT_TRUE(URIUtils::IsZIP("/path/to/zipfile.zip"));
+ EXPECT_TRUE(URIUtils::IsZIP("/path/to/zipfile.cbz"));
+ EXPECT_FALSE(URIUtils::IsZIP("/path/to/file"));
+ EXPECT_FALSE(URIUtils::IsZIP("zip://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsBluray)
+{
+ EXPECT_TRUE(URIUtils::IsBluray("bluray://path/to/file"));
+}
+
+TEST_F(TestURIUtils, AddSlashAtEnd)
+{
+ std::string ref, var;
+
+ ref = "bluray://path/to/file/";
+ var = "bluray://path/to/file/";
+ URIUtils::AddSlashAtEnd(var);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, HasSlashAtEnd)
+{
+ EXPECT_TRUE(URIUtils::HasSlashAtEnd("bluray://path/to/file/"));
+ EXPECT_FALSE(URIUtils::HasSlashAtEnd("bluray://path/to/file"));
+}
+
+TEST_F(TestURIUtils, RemoveSlashAtEnd)
+{
+ std::string ref, var;
+
+ ref = "bluray://path/to/file";
+ var = "bluray://path/to/file/";
+ URIUtils::RemoveSlashAtEnd(var);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, CreateArchivePath)
+{
+ std::string ref, var;
+
+ ref = "zip://%2fpath%2fto%2f/file";
+ var = URIUtils::CreateArchivePath("zip", CURL("/path/to/"), "file").Get();
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, AddFileToFolder)
+{
+ std::string ref = "/path/to/file";
+ std::string var = URIUtils::AddFileToFolder("/path/to", "file");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "/path/to/file/and/more";
+ var = URIUtils::AddFileToFolder("/path", "to", "file", "and", "more");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, HasParentInHostname)
+{
+ EXPECT_TRUE(URIUtils::HasParentInHostname(CURL("zip://")));
+ EXPECT_TRUE(URIUtils::HasParentInHostname(CURL("bluray://")));
+}
+
+TEST_F(TestURIUtils, HasEncodedHostname)
+{
+ EXPECT_TRUE(URIUtils::HasEncodedHostname(CURL("zip://")));
+ EXPECT_TRUE(URIUtils::HasEncodedHostname(CURL("bluray://")));
+ EXPECT_TRUE(URIUtils::HasEncodedHostname(CURL("musicsearch://")));
+}
+
+TEST_F(TestURIUtils, HasEncodedFilename)
+{
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("shout://")));
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("dav://")));
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("rss://")));
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("davs://")));
+}
+
+TEST_F(TestURIUtils, GetRealPath)
+{
+ std::string ref;
+
+ ref = "/path/to/file/";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+
+ ref = "path/to/file";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("../path/to/file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("./path/to/file").c_str());
+
+ ref = "/path/to/file";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/path/to/./file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/./path/to/./file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/path/to/some/../file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/../path/to/some/../file").c_str());
+
+ ref = "/path/to";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/path/to/some/../file/..").c_str());
+
+#ifdef TARGET_WINDOWS
+ ref = "\\\\path\\to\\file\\";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+
+ ref = "path\\to\\file";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("..\\path\\to\\file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(".\\path\\to\\file").c_str());
+
+ ref = "\\\\path\\to\\file";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\path\\to\\.\\file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\.\\path/to\\.\\file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\path\\to\\some\\..\\file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\..\\path\\to\\some\\..\\file").c_str());
+
+ ref = "\\\\path\\to";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\path\\to\\some\\..\\file\\..").c_str());
+#endif
+
+ // test rar/zip paths
+ ref = "zip://%2fpath%2fto%2fzip/subpath/to/file";
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+
+ // test rar/zip paths
+ ref = "zip://%2fpath%2fto%2fzip/subpath/to/file";
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("zip://%2fpath%2fto%2fzip/../subpath/to/file").c_str());
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("zip://%2fpath%2fto%2fzip/./subpath/to/file").c_str());
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("zip://%2fpath%2fto%2fzip/subpath/to/./file").c_str());
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("zip://%2fpath%2fto%2fzip/subpath/to/some/../file").c_str());
+
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("zip://%2fpath%2fto%2f.%2fzip/subpath/to/file").c_str());
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("zip://%2fpath%2fto%2fsome%2f..%2fzip/subpath/to/file").c_str());
+
+ // test zip/zip path
+ ref ="zip://zip%3a%2f%2f%252Fpath%252Fto%252Fzip%2fpath%2fto%2fzip/subpath/to/file";
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("zip://zip%3a%2f%2f%252Fpath%252Fto%252Fsome%252F..%252Fzip%2fpath%2fto%2fsome%2f..%2fzip/subpath/to/some/../file").c_str());
+}
+
+TEST_F(TestURIUtils, UpdateUrlEncoding)
+{
+ std::string oldUrl = "stack://zip://%2fpath%2fto%2farchive%2fsome%2darchive%2dfile%2eCD1%2ezip/video.avi , zip://%2fpath%2fto%2farchive%2fsome%2darchive%2dfile%2eCD2%2ezip/video.avi";
+ std::string newUrl = "stack://zip://%2fpath%2fto%2farchive%2fsome-archive-file.CD1.zip/video.avi , zip://%2fpath%2fto%2farchive%2fsome-archive-file.CD2.zip/video.avi";
+
+ EXPECT_TRUE(URIUtils::UpdateUrlEncoding(oldUrl));
+ EXPECT_STRCASEEQ(newUrl.c_str(), oldUrl.c_str());
+
+ oldUrl = "zip://%2fpath%2fto%2farchive%2fsome%2darchive%2efile%2ezip/video.avi";
+ newUrl = "zip://%2fpath%2fto%2farchive%2fsome-archive.file.zip/video.avi";
+
+ EXPECT_TRUE(URIUtils::UpdateUrlEncoding(oldUrl));
+ EXPECT_STRCASEEQ(newUrl.c_str(), oldUrl.c_str());
+
+ oldUrl = "/path/to/some/long%2dnamed%2efile";
+ newUrl = "/path/to/some/long%2dnamed%2efile";
+
+ EXPECT_FALSE(URIUtils::UpdateUrlEncoding(oldUrl));
+ EXPECT_STRCASEEQ(newUrl.c_str(), oldUrl.c_str());
+
+ oldUrl = "/path/to/some/long-named.file";
+ newUrl = "/path/to/some/long-named.file";
+
+ EXPECT_FALSE(URIUtils::UpdateUrlEncoding(oldUrl));
+ EXPECT_STRCASEEQ(newUrl.c_str(), oldUrl.c_str());
+}
diff --git a/xbmc/utils/test/TestUrlOptions.cpp b/xbmc/utils/test/TestUrlOptions.cpp
new file mode 100644
index 0000000..f684fe5
--- /dev/null
+++ b/xbmc/utils/test/TestUrlOptions.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/UrlOptions.h"
+#include "utils/Variant.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestUrlOptions, Clear)
+{
+ const char *key = "foo";
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(key, "bar");
+ EXPECT_TRUE(urlOptions.HasOption(key));
+
+ urlOptions.Clear();
+ EXPECT_FALSE(urlOptions.HasOption(key));
+}
+
+TEST(TestUrlOptions, AddOption)
+{
+ const char *keyChar = "char";
+ const char *keyString = "string";
+ const char *keyEmpty = "empty";
+ const char *keyInt = "int";
+ const char *keyFloat = "float";
+ const char *keyDouble = "double";
+ const char *keyBool = "bool";
+
+ const char *valueChar = "valueChar";
+ const std::string valueString = "valueString";
+ const char *valueEmpty = "";
+ int valueInt = 1;
+ float valueFloat = 1.0f;
+ double valueDouble = 1.0;
+ bool valueBool = true;
+
+ CVariant variantValue;
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(keyChar, valueChar);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyChar, variantValue));
+ EXPECT_TRUE(variantValue.isString());
+ EXPECT_STREQ(valueChar, variantValue.asString().c_str());
+ }
+
+ urlOptions.AddOption(keyString, valueString);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyString, variantValue));
+ EXPECT_TRUE(variantValue.isString());
+ EXPECT_STREQ(valueString.c_str(), variantValue.asString().c_str());
+ }
+
+ urlOptions.AddOption(keyEmpty, valueEmpty);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyEmpty, variantValue));
+ EXPECT_TRUE(variantValue.isString());
+ EXPECT_STREQ(valueEmpty, variantValue.asString().c_str());
+ }
+
+ urlOptions.AddOption(keyInt, valueInt);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyInt, variantValue));
+ EXPECT_TRUE(variantValue.isInteger());
+ EXPECT_EQ(valueInt, (int)variantValue.asInteger());
+ }
+
+ urlOptions.AddOption(keyFloat, valueFloat);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyFloat, variantValue));
+ EXPECT_TRUE(variantValue.isDouble());
+ EXPECT_EQ(valueFloat, variantValue.asFloat());
+ }
+
+ urlOptions.AddOption(keyDouble, valueDouble);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyDouble, variantValue));
+ EXPECT_TRUE(variantValue.isDouble());
+ EXPECT_EQ(valueDouble, variantValue.asDouble());
+ }
+
+ urlOptions.AddOption(keyBool, valueBool);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyBool, variantValue));
+ EXPECT_TRUE(variantValue.isBoolean());
+ EXPECT_EQ(valueBool, variantValue.asBoolean());
+ }
+}
+
+TEST(TestUrlOptions, AddOptions)
+{
+ std::string ref = "foo=bar&key=value";
+
+ CUrlOptions urlOptions(ref);
+ {
+ CVariant value;
+ EXPECT_TRUE(urlOptions.GetOption("foo", value));
+ EXPECT_TRUE(value.isString());
+ EXPECT_STREQ("bar", value.asString().c_str());
+ }
+ {
+ CVariant value;
+ EXPECT_TRUE(urlOptions.GetOption("key", value));
+ EXPECT_TRUE(value.isString());
+ EXPECT_STREQ("value", value.asString().c_str());
+ }
+
+ ref = "foo=bar&key";
+ urlOptions.Clear();
+ urlOptions.AddOptions(ref);
+ {
+ CVariant value;
+ EXPECT_TRUE(urlOptions.GetOption("foo", value));
+ EXPECT_TRUE(value.isString());
+ EXPECT_STREQ("bar", value.asString().c_str());
+ }
+ {
+ CVariant value;
+ EXPECT_TRUE(urlOptions.GetOption("key", value));
+ EXPECT_TRUE(value.isString());
+ EXPECT_TRUE(value.empty());
+ }
+}
+
+TEST(TestUrlOptions, RemoveOption)
+{
+ const char *key = "foo";
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(key, "bar");
+ EXPECT_TRUE(urlOptions.HasOption(key));
+
+ urlOptions.RemoveOption(key);
+ EXPECT_FALSE(urlOptions.HasOption(key));
+}
+
+TEST(TestUrlOptions, HasOption)
+{
+ const char *key = "foo";
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(key, "bar");
+ EXPECT_TRUE(urlOptions.HasOption(key));
+ EXPECT_FALSE(urlOptions.HasOption("bar"));
+}
+
+TEST(TestUrlOptions, GetOptions)
+{
+ const char *key1 = "foo";
+ const char *key2 = "key";
+ const char *value1 = "bar";
+ const char *value2 = "value";
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(key1, value1);
+ urlOptions.AddOption(key2, value2);
+ const CUrlOptions::UrlOptions &options = urlOptions.GetOptions();
+ EXPECT_FALSE(options.empty());
+ EXPECT_EQ(2U, options.size());
+
+ CUrlOptions::UrlOptions::const_iterator it1 = options.find(key1);
+ EXPECT_TRUE(it1 != options.end());
+ CUrlOptions::UrlOptions::const_iterator it2 = options.find(key2);
+ EXPECT_TRUE(it2 != options.end());
+ EXPECT_FALSE(options.find("wrong") != options.end());
+ EXPECT_TRUE(it1->second.isString());
+ EXPECT_TRUE(it2->second.isString());
+ EXPECT_STREQ(value1, it1->second.asString().c_str());
+ EXPECT_STREQ(value2, it2->second.asString().c_str());
+}
+
+TEST(TestUrlOptions, GetOptionsString)
+{
+ const char *ref = "foo=bar&key";
+
+ CUrlOptions urlOptions(ref);
+ std::string value = urlOptions.GetOptionsString();
+ EXPECT_STREQ(ref, value.c_str());
+}
diff --git a/xbmc/utils/test/TestVariant.cpp b/xbmc/utils/test/TestVariant.cpp
new file mode 100644
index 0000000..3c96cd0
--- /dev/null
+++ b/xbmc/utils/test/TestVariant.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/Variant.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestVariant, VariantTypeInteger)
+{
+ CVariant a((int)0), b((int64_t)1);
+
+ EXPECT_TRUE(a.isInteger());
+ EXPECT_EQ(CVariant::VariantTypeInteger, a.type());
+ EXPECT_TRUE(b.isInteger());
+ EXPECT_EQ(CVariant::VariantTypeInteger, b.type());
+
+ EXPECT_EQ((int64_t)1, b.asInteger());
+}
+
+TEST(TestVariant, VariantTypeUnsignedInteger)
+{
+ CVariant a((unsigned int)0), b((uint64_t)1);
+
+ EXPECT_TRUE(a.isUnsignedInteger());
+ EXPECT_EQ(CVariant::VariantTypeUnsignedInteger, a.type());
+ EXPECT_TRUE(b.isUnsignedInteger());
+ EXPECT_EQ(CVariant::VariantTypeUnsignedInteger, b.type());
+
+ EXPECT_EQ((uint64_t)1, b.asUnsignedInteger());
+}
+
+TEST(TestVariant, VariantTypeBoolean)
+{
+ CVariant a(true);
+
+ EXPECT_TRUE(a.isBoolean());
+ EXPECT_EQ(CVariant::VariantTypeBoolean, a.type());
+
+ EXPECT_TRUE(a.asBoolean());
+}
+
+TEST(TestVariant, VariantTypeString)
+{
+ CVariant a("VariantTypeString");
+ CVariant b("VariantTypeString2", sizeof("VariantTypeString2") - 1);
+ std::string str("VariantTypeString3");
+ CVariant c(str);
+
+ EXPECT_TRUE(a.isString());
+ EXPECT_EQ(CVariant::VariantTypeString, a.type());
+ EXPECT_TRUE(b.isString());
+ EXPECT_EQ(CVariant::VariantTypeString, b.type());
+ EXPECT_TRUE(c.isString());
+ EXPECT_EQ(CVariant::VariantTypeString, c.type());
+
+ EXPECT_STREQ("VariantTypeString", a.asString().c_str());
+ EXPECT_STREQ("VariantTypeString2", b.asString().c_str());
+ EXPECT_STREQ("VariantTypeString3", c.asString().c_str());
+}
+
+TEST(TestVariant, VariantTypeWideString)
+{
+ CVariant a(L"VariantTypeWideString");
+ CVariant b(L"VariantTypeWideString2", sizeof(L"VariantTypeWideString2") - 1);
+ std::wstring str(L"VariantTypeWideString3");
+ CVariant c(str);
+
+ EXPECT_TRUE(a.isWideString());
+ EXPECT_EQ(CVariant::VariantTypeWideString, a.type());
+ EXPECT_TRUE(b.isWideString());
+ EXPECT_EQ(CVariant::VariantTypeWideString, b.type());
+ EXPECT_TRUE(c.isWideString());
+ EXPECT_EQ(CVariant::VariantTypeWideString, c.type());
+
+ EXPECT_STREQ(L"VariantTypeWideString", a.asWideString().c_str());
+ EXPECT_STREQ(L"VariantTypeWideString2", b.asWideString().c_str());
+ EXPECT_STREQ(L"VariantTypeWideString3", c.asWideString().c_str());
+}
+
+TEST(TestVariant, VariantTypeDouble)
+{
+ CVariant a((float)0.0f), b((double)0.1f);
+
+ EXPECT_TRUE(a.isDouble());
+ EXPECT_EQ(CVariant::VariantTypeDouble, a.type());
+ EXPECT_TRUE(b.isDouble());
+ EXPECT_EQ(CVariant::VariantTypeDouble, b.type());
+
+ EXPECT_EQ((float)0.0f, a.asDouble());
+ EXPECT_EQ((double)0.1f, b.asDouble());
+}
+
+TEST(TestVariant, VariantTypeArray)
+{
+ std::vector<std::string> strarray;
+ strarray.emplace_back("string1");
+ strarray.emplace_back("string2");
+ strarray.emplace_back("string3");
+ strarray.emplace_back("string4");
+ CVariant a(strarray);
+
+ EXPECT_TRUE(a.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, a.type());
+}
+
+TEST(TestVariant, VariantTypeObject)
+{
+ CVariant a;
+ a["key"] = "value";
+
+ EXPECT_TRUE(a.isObject());
+ EXPECT_EQ(CVariant::VariantTypeObject, a.type());
+}
+
+TEST(TestVariant, VariantTypeNull)
+{
+ CVariant a;
+
+ EXPECT_TRUE(a.isNull());
+ EXPECT_EQ(CVariant::VariantTypeNull, a.type());
+}
+
+TEST(TestVariant, VariantFromMap)
+{
+ std::map<std::string, std::string> strMap;
+ strMap["key"] = "value";
+ CVariant a = strMap;
+
+ EXPECT_TRUE(a.isObject());
+ EXPECT_TRUE(a.size() == 1);
+ EXPECT_EQ(CVariant::VariantTypeObject, a.type());
+ EXPECT_TRUE(a.isMember("key"));
+ EXPECT_TRUE(a["key"].isString());
+ EXPECT_STREQ(a["key"].asString().c_str(), "value");
+
+ std::map<std::string, CVariant> variantMap;
+ variantMap["key"] = CVariant("value");
+ CVariant b = variantMap;
+
+ EXPECT_TRUE(b.isObject());
+ EXPECT_TRUE(b.size() == 1);
+ EXPECT_EQ(CVariant::VariantTypeObject, b.type());
+ EXPECT_TRUE(b.isMember("key"));
+ EXPECT_TRUE(b["key"].isString());
+ EXPECT_STREQ(b["key"].asString().c_str(), "value");
+}
+
+TEST(TestVariant, operatorTest)
+{
+ std::vector<std::string> strarray;
+ strarray.emplace_back("string1");
+ CVariant a, b, c(strarray), d;
+ a["key"] = "value";
+ b = a;
+ c[0] = "value2";
+ d = c;
+
+ EXPECT_TRUE(a.isObject());
+ EXPECT_EQ(CVariant::VariantTypeObject, a.type());
+ EXPECT_TRUE(b.isObject());
+ EXPECT_EQ(CVariant::VariantTypeObject, b.type());
+ EXPECT_TRUE(c.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, c.type());
+ EXPECT_TRUE(d.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, d.type());
+
+ EXPECT_TRUE(a == b);
+ EXPECT_TRUE(c == d);
+ EXPECT_FALSE(a == d);
+
+ EXPECT_STREQ("value", a["key"].asString().c_str());
+ EXPECT_STREQ("value2", c[0].asString().c_str());
+}
+
+TEST(TestVariant, push_back)
+{
+ CVariant a, b("variant1"), c("variant2"), d("variant3");
+ a.push_back(b);
+ a.push_back(c);
+ a.push_back(d);
+
+ EXPECT_TRUE(a.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, a.type());
+ EXPECT_STREQ("variant1", a[0].asString().c_str());
+ EXPECT_STREQ("variant2", a[1].asString().c_str());
+ EXPECT_STREQ("variant3", a[2].asString().c_str());
+}
+
+TEST(TestVariant, append)
+{
+ CVariant a, b("variant1"), c("variant2"), d("variant3");
+ a.append(b);
+ a.append(c);
+ a.append(d);
+
+ EXPECT_TRUE(a.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, a.type());
+ EXPECT_STREQ("variant1", a[0].asString().c_str());
+ EXPECT_STREQ("variant2", a[1].asString().c_str());
+ EXPECT_STREQ("variant3", a[2].asString().c_str());
+}
+
+TEST(TestVariant, c_str)
+{
+ CVariant a("variant");
+
+ EXPECT_STREQ("variant", a.c_str());
+}
+
+TEST(TestVariant, swap)
+{
+ CVariant a((int)0), b("variant");
+
+ EXPECT_TRUE(a.isInteger());
+ EXPECT_TRUE(b.isString());
+
+ a.swap(b);
+ EXPECT_TRUE(b.isInteger());
+ EXPECT_TRUE(a.isString());
+}
+
+TEST(TestVariant, iterator_array)
+{
+ std::vector<std::string> strarray;
+ strarray.emplace_back("string");
+ strarray.emplace_back("string");
+ strarray.emplace_back("string");
+ strarray.emplace_back("string");
+ CVariant a(strarray);
+
+ EXPECT_TRUE(a.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, a.type());
+
+ for (auto it = a.begin_array(); it != a.end_array(); it++)
+ {
+ EXPECT_STREQ("string", it->c_str());
+ }
+
+ for (auto const_it = a.begin_array(); const_it != a.end_array(); const_it++)
+ {
+ EXPECT_STREQ("string", const_it->c_str());
+ }
+}
+
+TEST(TestVariant, iterator_map)
+{
+ CVariant a;
+ a["key1"] = "string";
+ a["key2"] = "string";
+ a["key3"] = "string";
+ a["key4"] = "string";
+
+ EXPECT_TRUE(a.isObject());
+ EXPECT_EQ(CVariant::VariantTypeObject, a.type());
+
+ for (auto it = a.begin_map(); it != a.end_map(); it++)
+ {
+ EXPECT_STREQ("string", it->second.c_str());
+ }
+
+ for (auto const_it = a.begin_map(); const_it != a.end_map(); const_it++)
+ {
+ EXPECT_STREQ("string", const_it->second.c_str());
+ }
+}
+
+TEST(TestVariant, size)
+{
+ std::vector<std::string> strarray;
+ strarray.emplace_back("string");
+ strarray.emplace_back("string");
+ strarray.emplace_back("string");
+ strarray.emplace_back("string");
+ CVariant a(strarray);
+
+ EXPECT_EQ((unsigned int)4, a.size());
+}
+
+TEST(TestVariant, empty)
+{
+ std::vector<std::string> strarray;
+ CVariant a(strarray);
+
+ EXPECT_TRUE(a.empty());
+}
+
+TEST(TestVariant, clear)
+{
+ std::vector<std::string> strarray;
+ strarray.emplace_back("string");
+ strarray.emplace_back("string");
+ strarray.emplace_back("string");
+ strarray.emplace_back("string");
+ CVariant a(strarray);
+
+ EXPECT_FALSE(a.empty());
+ a.clear();
+ EXPECT_TRUE(a.empty());
+}
+
+TEST(TestVariant, erase)
+{
+ std::vector<std::string> strarray;
+ strarray.emplace_back("string1");
+ strarray.emplace_back("string2");
+ strarray.emplace_back("string3");
+ strarray.emplace_back("string4");
+ CVariant a, b(strarray);
+ a["key1"] = "string1";
+ a["key2"] = "string2";
+ a["key3"] = "string3";
+ a["key4"] = "string4";
+
+ EXPECT_STREQ("string2", a["key2"].c_str());
+ EXPECT_STREQ("string2", b[1].c_str());
+ a.erase("key2");
+ b.erase(1);
+ EXPECT_FALSE(a["key2"].c_str());
+ EXPECT_STREQ("string3", b[1].c_str());
+}
+
+TEST(TestVariant, isMember)
+{
+ CVariant a;
+ a["key1"] = "string1";
+
+ EXPECT_TRUE(a.isMember("key1"));
+ EXPECT_FALSE(a.isMember("key2"));
+}
diff --git a/xbmc/utils/test/TestXBMCTinyXML.cpp b/xbmc/utils/test/TestXBMCTinyXML.cpp
new file mode 100644
index 0000000..b3f84eb
--- /dev/null
+++ b/xbmc/utils/test/TestXBMCTinyXML.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "test/TestUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestXBMCTinyXML, ParseFromString)
+{
+ bool retval = false;
+ // scraper results with unescaped &
+ CXBMCTinyXML doc;
+ std::string data("<details><url function=\"ParseTMDBRating\" "
+ "cache=\"tmdb-en-12244.json\">"
+ "http://api.themoviedb.org/3/movie/12244"
+ "?api_key=57983e31fb435df4df77afb854740ea9"
+ "&language=en&#x3f;&#x003F;&#0063;</url></details>");
+ doc.Parse(data);
+ TiXmlNode *root = doc.RootElement();
+ if (root && root->ValueStr() == "details")
+ {
+ TiXmlElement *url = root->FirstChildElement("url");
+ if (url && url->FirstChild())
+ {
+ retval = (url->FirstChild()->ValueStr() == "http://api.themoviedb.org/3/movie/12244?api_key=57983e31fb435df4df77afb854740ea9&language=en???");
+ }
+ }
+ EXPECT_TRUE(retval);
+}
+
+TEST(TestXBMCTinyXML, ParseFromFileHandle)
+{
+ bool retval = false;
+ // scraper results with unescaped &
+ CXBMCTinyXML doc;
+ FILE *f = fopen(XBMC_REF_FILE_PATH("/xbmc/utils/test/CXBMCTinyXML-test.xml").c_str(), "r");
+ ASSERT_NE(nullptr, f);
+ doc.LoadFile(f);
+ fclose(f);
+ TiXmlNode *root = doc.RootElement();
+ if (root && root->ValueStr() == "details")
+ {
+ TiXmlElement *url = root->FirstChildElement("url");
+ if (url && url->FirstChild())
+ {
+ std::string str = url->FirstChild()->ValueStr();
+ retval = (StringUtils::Trim(str) == "http://api.themoviedb.org/3/movie/12244?api_key=57983e31fb435df4df77afb854740ea9&language=en???");
+ }
+ }
+ EXPECT_TRUE(retval);
+}
diff --git a/xbmc/utils/test/TestXMLUtils.cpp b/xbmc/utils/test/TestXMLUtils.cpp
new file mode 100644
index 0000000..ba4c87c
--- /dev/null
+++ b/xbmc/utils/test/TestXMLUtils.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "XBDateTime.h"
+#include "utils/StringUtils.h"
+#include "utils/XMLUtils.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestXMLUtils, GetHex)
+{
+ CXBMCTinyXML a;
+ uint32_t ref, val;
+
+ a.Parse(std::string("<root><node>0xFF</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetHex(a.RootElement(), "node", val));
+
+ ref = 0xFF;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetUInt)
+{
+ CXBMCTinyXML a;
+ uint32_t ref, val;
+
+ a.Parse(std::string("<root><node>1000</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetUInt(a.RootElement(), "node", val));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetLong)
+{
+ CXBMCTinyXML a;
+ long ref, val;
+
+ a.Parse(std::string("<root><node>1000</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetLong(a.RootElement(), "node", val));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetFloat)
+{
+ CXBMCTinyXML a;
+ float ref, val;
+
+ a.Parse(std::string("<root><node>1000.1f</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetFloat(a.RootElement(), "node", val));
+ EXPECT_TRUE(XMLUtils::GetFloat(a.RootElement(), "node", val, 1000.0f,
+ 1000.2f));
+ ref = 1000.1f;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetDouble)
+{
+ CXBMCTinyXML a;
+ double val;
+ std::string refstr, valstr;
+
+ a.Parse(std::string("<root><node>1000.1f</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetDouble(a.RootElement(), "node", val));
+
+ refstr = "1000.100000";
+ valstr = StringUtils::Format("{:f}", val);
+ EXPECT_STREQ(refstr.c_str(), valstr.c_str());
+}
+
+TEST(TestXMLUtils, GetInt)
+{
+ CXBMCTinyXML a;
+ int ref, val;
+
+ a.Parse(std::string("<root><node>1000</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetInt(a.RootElement(), "node", val));
+ EXPECT_TRUE(XMLUtils::GetInt(a.RootElement(), "node", val, 999, 1001));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetBoolean)
+{
+ CXBMCTinyXML a;
+ bool ref, val;
+
+ a.Parse(std::string("<root><node>true</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetBoolean(a.RootElement(), "node", val));
+
+ ref = true;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetString)
+{
+ CXBMCTinyXML a;
+ std::string ref, val;
+
+ a.Parse(std::string("<root><node>some string</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetString(a.RootElement(), "node", val));
+
+ ref = "some string";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, GetAdditiveString)
+{
+ CXBMCTinyXML a, b;
+ std::string ref, val;
+
+ a.Parse(std::string("<root>\n"
+ " <node>some string1</node>\n"
+ " <node>some string2</node>\n"
+ " <node>some string3</node>\n"
+ " <node>some string4</node>\n"
+ " <node>some string5</node>\n"
+ "</root>\n"));
+ EXPECT_TRUE(XMLUtils::GetAdditiveString(a.RootElement(), "node", ",", val));
+
+ ref = "some string1,some string2,some string3,some string4,some string5";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+
+ val.clear();
+ b.Parse(std::string("<root>\n"
+ " <node>some string1</node>\n"
+ " <node>some string2</node>\n"
+ " <node clear=\"true\">some string3</node>\n"
+ " <node>some string4</node>\n"
+ " <node>some string5</node>\n"
+ "</root>\n"));
+ EXPECT_TRUE(XMLUtils::GetAdditiveString(b.RootElement(), "node", ",", val));
+
+ ref = "some string3,some string4,some string5";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, GetStringArray)
+{
+ CXBMCTinyXML a;
+ std::vector<std::string> strarray;
+
+ a.Parse(std::string("<root>\n"
+ " <node>some string1</node>\n"
+ " <node>some string2</node>\n"
+ " <node>some string3</node>\n"
+ " <node>some string4</node>\n"
+ " <node>some string5</node>\n"
+ "</root>\n"));
+ EXPECT_TRUE(XMLUtils::GetStringArray(a.RootElement(), "node", strarray));
+
+ EXPECT_STREQ("some string1", strarray.at(0).c_str());
+ EXPECT_STREQ("some string2", strarray.at(1).c_str());
+ EXPECT_STREQ("some string3", strarray.at(2).c_str());
+ EXPECT_STREQ("some string4", strarray.at(3).c_str());
+ EXPECT_STREQ("some string5", strarray.at(4).c_str());
+}
+
+TEST(TestXMLUtils, GetPath)
+{
+ CXBMCTinyXML a, b;
+ std::string ref, val;
+
+ a.Parse(std::string("<root><node urlencoded=\"yes\">special://xbmc/</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetPath(a.RootElement(), "node", val));
+
+ ref = "special://xbmc/";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+
+ val.clear();
+ b.Parse(std::string("<root><node>special://xbmcbin/</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetPath(b.RootElement(), "node", val));
+
+ ref = "special://xbmcbin/";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, GetDate)
+{
+ CXBMCTinyXML a;
+ CDateTime ref, val;
+
+ a.Parse(std::string("<root><node>2012-07-08</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetDate(a.RootElement(), "node", val));
+ ref.SetDate(2012, 7, 8);
+ EXPECT_TRUE(ref == val);
+}
+
+TEST(TestXMLUtils, GetDateTime)
+{
+ CXBMCTinyXML a;
+ CDateTime ref, val;
+
+ a.Parse(std::string("<root><node>2012-07-08 01:02:03</node></root>"));
+ EXPECT_TRUE(XMLUtils::GetDateTime(a.RootElement(), "node", val));
+ ref.SetDateTime(2012, 7, 8, 1, 2, 3);
+ EXPECT_TRUE(ref == val);
+}
+
+TEST(TestXMLUtils, SetString)
+{
+ CXBMCTinyXML a;
+ std::string ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ XMLUtils::SetString(a.RootElement(), "node", "some string");
+ EXPECT_TRUE(XMLUtils::GetString(a.RootElement(), "node", val));
+
+ ref = "some string";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, SetAdditiveString)
+{
+ CXBMCTinyXML a;
+ std::string ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ XMLUtils::SetAdditiveString(a.RootElement(), "node", ",",
+ "some string1,some string2,some string3,some string4,some string5");
+ EXPECT_TRUE(XMLUtils::GetAdditiveString(a.RootElement(), "node", ",", val));
+
+ ref = "some string1,some string2,some string3,some string4,some string5";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, SetStringArray)
+{
+ CXBMCTinyXML a;
+ std::vector<std::string> strarray;
+ strarray.emplace_back("some string1");
+ strarray.emplace_back("some string2");
+ strarray.emplace_back("some string3");
+ strarray.emplace_back("some string4");
+ strarray.emplace_back("some string5");
+
+ a.Parse(std::string("<root></root>"));
+ XMLUtils::SetStringArray(a.RootElement(), "node", strarray);
+ EXPECT_TRUE(XMLUtils::GetStringArray(a.RootElement(), "node", strarray));
+
+ EXPECT_STREQ("some string1", strarray.at(0).c_str());
+ EXPECT_STREQ("some string2", strarray.at(1).c_str());
+ EXPECT_STREQ("some string3", strarray.at(2).c_str());
+ EXPECT_STREQ("some string4", strarray.at(3).c_str());
+ EXPECT_STREQ("some string5", strarray.at(4).c_str());
+}
+
+TEST(TestXMLUtils, SetInt)
+{
+ CXBMCTinyXML a;
+ int ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ XMLUtils::SetInt(a.RootElement(), "node", 1000);
+ EXPECT_TRUE(XMLUtils::GetInt(a.RootElement(), "node", val));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetFloat)
+{
+ CXBMCTinyXML a;
+ float ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ XMLUtils::SetFloat(a.RootElement(), "node", 1000.1f);
+ EXPECT_TRUE(XMLUtils::GetFloat(a.RootElement(), "node", val));
+
+ ref = 1000.1f;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetBoolean)
+{
+ CXBMCTinyXML a;
+ bool ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ XMLUtils::SetBoolean(a.RootElement(), "node", true);
+ EXPECT_TRUE(XMLUtils::GetBoolean(a.RootElement(), "node", val));
+
+ ref = true;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetHex)
+{
+ CXBMCTinyXML a;
+ uint32_t ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ XMLUtils::SetHex(a.RootElement(), "node", 0xFF);
+ EXPECT_TRUE(XMLUtils::GetHex(a.RootElement(), "node", val));
+
+ ref = 0xFF;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetPath)
+{
+ CXBMCTinyXML a;
+ std::string ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ XMLUtils::SetPath(a.RootElement(), "node", "special://xbmc/");
+ EXPECT_TRUE(XMLUtils::GetPath(a.RootElement(), "node", val));
+
+ ref = "special://xbmc/";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, SetLong)
+{
+ CXBMCTinyXML a;
+ long ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ XMLUtils::SetLong(a.RootElement(), "node", 1000);
+ EXPECT_TRUE(XMLUtils::GetLong(a.RootElement(), "node", val));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetDate)
+{
+ CXBMCTinyXML a;
+ CDateTime ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ ref.SetDate(2012, 7, 8);
+ XMLUtils::SetDate(a.RootElement(), "node", ref);
+ EXPECT_TRUE(XMLUtils::GetDate(a.RootElement(), "node", val));
+ EXPECT_TRUE(ref == val);
+}
+
+TEST(TestXMLUtils, SetDateTime)
+{
+ CXBMCTinyXML a;
+ CDateTime ref, val;
+
+ a.Parse(std::string("<root></root>"));
+ ref.SetDateTime(2012, 7, 8, 1, 2, 3);
+ XMLUtils::SetDateTime(a.RootElement(), "node", ref);
+ EXPECT_TRUE(XMLUtils::GetDateTime(a.RootElement(), "node", val));
+ EXPECT_TRUE(ref == val);
+}
diff --git a/xbmc/utils/test/Testlog.cpp b/xbmc/utils/test/Testlog.cpp
new file mode 100644
index 0000000..a700d2a
--- /dev/null
+++ b/xbmc/utils/test/Testlog.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "CompileInfo.h"
+#include "ServiceBroker.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "test/TestUtils.h"
+#include "utils/RegExp.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+
+class Testlog : public testing::Test
+{
+protected:
+ Testlog() = default;
+ ~Testlog() override { CServiceBroker::GetLogging().Deinitialize(); }
+};
+
+TEST_F(Testlog, Log)
+{
+ std::string logfile, logstring;
+ char buf[100];
+ ssize_t bytesread;
+ XFILE::CFile file;
+ CRegExp regex;
+
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
+ CServiceBroker::GetLogging().Initialize(
+ CSpecialProtocol::TranslatePath("special://temp/").c_str());
+ EXPECT_TRUE(XFILE::CFile::Exists(logfile));
+
+ CLog::Log(LOGDEBUG, "debug log message");
+ CLog::Log(LOGINFO, "info log message");
+ CLog::Log(LOGWARNING, "warning log message");
+ CLog::Log(LOGERROR, "error log message");
+ CLog::Log(LOGFATAL, "fatal log message");
+ CLog::Log(LOGNONE, "none type log message");
+ CServiceBroker::GetLogging().Deinitialize();
+
+ EXPECT_TRUE(file.Open(logfile));
+ while ((bytesread = file.Read(buf, sizeof(buf) - 1)) > 0)
+ {
+ buf[bytesread] = '\0';
+ logstring.append(buf);
+ }
+ file.Close();
+ EXPECT_FALSE(logstring.empty());
+
+ EXPECT_STREQ("\xEF\xBB\xBF", logstring.substr(0, 3).c_str());
+
+ EXPECT_TRUE(regex.RegComp(".*(debug|DEBUG) <general>: debug log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*(info|INFO) <general>: info log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*(warning|WARNING) <general>: warning log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*(error|ERROR) <general>: error log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*(critical|CRITICAL|fatal|FATAL) <general>: fatal log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*(off|OFF) <general>: none type log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+
+ EXPECT_TRUE(XFILE::CFile::Delete(logfile));
+}
+
+TEST_F(Testlog, SetLogLevel)
+{
+ std::string logfile;
+
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
+ CServiceBroker::GetLogging().Initialize(
+ CSpecialProtocol::TranslatePath("special://temp/").c_str());
+ EXPECT_TRUE(XFILE::CFile::Exists(logfile));
+
+ EXPECT_EQ(LOG_LEVEL_DEBUG, CServiceBroker::GetLogging().GetLogLevel());
+ CServiceBroker::GetLogging().SetLogLevel(LOG_LEVEL_MAX);
+ EXPECT_EQ(LOG_LEVEL_MAX, CServiceBroker::GetLogging().GetLogLevel());
+
+ CServiceBroker::GetLogging().Deinitialize();
+ EXPECT_TRUE(XFILE::CFile::Delete(logfile));
+}
diff --git a/xbmc/utils/test/Testrfft.cpp b/xbmc/utils/test/Testrfft.cpp
new file mode 100644
index 0000000..a6c859d
--- /dev/null
+++ b/xbmc/utils/test/Testrfft.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/rfft.h"
+
+#include <gtest/gtest.h>
+
+#if defined(TARGET_WINDOWS) && !defined(_USE_MATH_DEFINES)
+#define _USE_MATH_DEFINES
+#endif
+
+#include <math.h>
+
+
+TEST(TestRFFT, SimpleSignal)
+{
+ const int size = 32;
+ const int freq1 = 5;
+ const int freq2[] = {1,7};
+ std::vector<float> input(2*size);
+ std::vector<float> output(size);
+ for (size_t i=0;i<size;++i)
+ {
+ input[2*i] = cos(freq1*2.0*M_PI*i/size);
+ input[2*i+1] = cos(freq2[0]*2.0*M_PI*i/size)+cos(freq2[1]*2.0*M_PI*i/size);
+ }
+ RFFT transform(size, false);
+
+ transform.calc(&input[0], &output[0]);
+
+ for (int i=0;i<size/2;++i)
+ {
+ EXPECT_NEAR(output[2*i],(i==freq1?1.0:0.0), 1e-7);
+ EXPECT_NEAR(output[2*i+1], ((i==freq2[0]||i==freq2[1])?1.0:0.0), 1e-7);
+ }
+}
diff --git a/xbmc/utils/test/data/language/Spanish/strings.po b/xbmc/utils/test/data/language/Spanish/strings.po
new file mode 100644
index 0000000..8ee8d02
--- /dev/null
+++ b/xbmc/utils/test/data/language/Spanish/strings.po
@@ -0,0 +1,26 @@
+# Kodi Media Center language file
+msgid ""
+msgstr ""
+"Project-Id-Version: XBMC Main\n"
+"Report-Msgid-Bugs-To: http://trac.xbmc.org/\n"
+"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
+"Language-Team: Spanish (http://www.transifex.com/projects/p/xbmc-main/language/es/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "#0"
+msgid "Programs"
+msgstr "Programas"
+
+msgctxt "#1"
+msgid "Pictures"
+msgstr "Imágenes"
+
+msgctxt "#2"
+msgid "Music"
+msgstr "Música"