summaryrefslogtreecommitdiffstats
path: root/xbmc/test
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/test')
-rw-r--r--xbmc/test/CMakeLists.txt13
-rw-r--r--xbmc/test/MtTestUtils.h45
-rw-r--r--xbmc/test/TestBasicEnvironment.cpp122
-rw-r--r--xbmc/test/TestBasicEnvironment.h25
-rw-r--r--xbmc/test/TestDateTime.cpp660
-rw-r--r--xbmc/test/TestDateTimeSpan.cpp80
-rw-r--r--xbmc/test/TestFileItem.cpp132
-rw-r--r--xbmc/test/TestTextureUtils.cpp51
-rw-r--r--xbmc/test/TestURL.cpp72
-rw-r--r--xbmc/test/TestUtil.cpp87
-rw-r--r--xbmc/test/TestUtils.cpp324
-rw-r--r--xbmc/test/TestUtils.h102
-rw-r--r--xbmc/test/xbmc-test.cpp32
13 files changed, 1745 insertions, 0 deletions
diff --git a/xbmc/test/CMakeLists.txt b/xbmc/test/CMakeLists.txt
new file mode 100644
index 0000000..7187270
--- /dev/null
+++ b/xbmc/test/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(SOURCES TestBasicEnvironment.cpp
+ TestFileItem.cpp
+ TestTextureUtils.cpp
+ TestURL.cpp
+ TestUtil.cpp
+ TestUtils.cpp
+ TestDateTime.cpp
+ TestDateTimeSpan.cpp)
+
+set(HEADERS TestBasicEnvironment.h
+ TestUtils.h)
+
+core_add_test_library(xbmc_test)
diff --git a/xbmc/test/MtTestUtils.h b/xbmc/test/MtTestUtils.h
new file mode 100644
index 0000000..e41dd5e
--- /dev/null
+++ b/xbmc/test/MtTestUtils.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2005-2019 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 "threads/SystemClock.h"
+
+#include <chrono>
+#include <thread>
+
+namespace ConditionPoll
+{
+/**
+ * This is usually enough time for the condition to have occurred in a test.
+ */
+constexpr unsigned int defaultTimeout{20000};
+
+/**
+ * poll until the lambda returns true or the timeout occurs. If the timeout occurs then
+ * the function will return false. Otherwise it will return true.
+ */
+template<typename L> inline bool poll(unsigned int timeoutMillis, L lambda)
+{
+ XbmcThreads::EndTime<> endTime{std::chrono::milliseconds(timeoutMillis)};
+ bool lastValue = false;
+ while (!endTime.IsTimePast() && (lastValue = lambda()) == false)
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ return lastValue;
+}
+
+/**
+ * poll until the lambda returns true or the defaultTimeout occurs. If the timeout occurs then
+ * the function will return false. Otherwise it will return true.
+ */
+template<typename L> inline bool poll(L lambda)
+{
+ return poll(defaultTimeout, lambda);
+}
+
+}
diff --git a/xbmc/test/TestBasicEnvironment.cpp b/xbmc/test/TestBasicEnvironment.cpp
new file mode 100644
index 0000000..7a35776
--- /dev/null
+++ b/xbmc/test/TestBasicEnvironment.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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 "TestBasicEnvironment.h"
+
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "ServiceManager.h"
+#include "TestUtils.h"
+#include "application/AppEnvironment.h"
+#include "application/AppParams.h"
+#include "application/Application.h"
+#include "filesystem/Directory.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "messaging/ApplicationMessenger.h"
+#include "platform/Filesystem.h"
+#include "profiles/ProfileManager.h"
+#include "settings/SettingsComponent.h"
+#include "windowing/WinSystem.h"
+
+#ifdef TARGET_DARWIN
+#include "Util.h"
+#endif
+
+#include <cstdio>
+#include <cstdlib>
+#include <climits>
+#include <system_error>
+
+namespace fs = KODI::PLATFORM::FILESYSTEM;
+
+TestBasicEnvironment::TestBasicEnvironment() = default;
+
+void TestBasicEnvironment::SetUp()
+{
+ const auto params = std::make_shared<CAppParams>();
+ params->SetPlatformDirectories(false);
+
+ CAppEnvironment::SetUp(params);
+
+ CServiceBroker::RegisterAppMessenger(std::make_shared<KODI::MESSAGING::CApplicationMessenger>());
+
+ XFILE::CFile *f;
+
+ g_application.m_ServiceManager.reset(new CServiceManager());
+
+ if (!CXBMCTestUtils::Instance().SetReferenceFileBasePath())
+ SetUpError();
+ CXBMCTestUtils::Instance().setTestFileFactoryWriteInputFile(
+ XBMC_REF_FILE_PATH("xbmc/filesystem/test/reffile.txt")
+ );
+
+//for darwin set framework path - else we get assert
+//in guisettings init below
+#ifdef TARGET_DARWIN
+ std::string frameworksPath = CUtil::GetFrameworksPath();
+ CSpecialProtocol::SetXBMCFrameworksPath(frameworksPath);
+#endif
+ /**
+ * @todo Something should be done about all the asserts in GUISettings so
+ * that the initialization of these components won't be needed.
+ */
+
+ /* Create a temporary directory and set it to be used throughout the
+ * test suite run.
+ */
+
+ std::error_code ec;
+ m_tempPath = fs::create_temp_directory(ec);
+ if (ec)
+ {
+ TearDown();
+ SetUpError();
+ }
+
+ CSpecialProtocol::SetTempPath(m_tempPath);
+ CSpecialProtocol::SetProfilePath(m_tempPath);
+
+ /* Create and delete a tempfile to initialize the VFS (really to initialize
+ * CLibcdio). This is done so that the initialization of the VFS does not
+ * affect the performance results of the test cases.
+ */
+ /** @todo Make the initialization of the VFS here optional so it can be
+ * testable in a test case.
+ */
+ f = XBMC_CREATETEMPFILE("");
+ if (!f || !XBMC_DELETETEMPFILE(f))
+ {
+ TearDown();
+ SetUpError();
+ }
+
+ const CProfile profile("special://temp");
+ CServiceBroker::GetSettingsComponent()->GetProfileManager()->AddProfile(profile);
+ CServiceBroker::GetSettingsComponent()->GetProfileManager()->CreateProfileFolders();
+
+ if (!g_application.m_ServiceManager->InitForTesting())
+ exit(1);
+}
+
+void TestBasicEnvironment::TearDown()
+{
+ XFILE::CDirectory::RemoveRecursive(m_tempPath);
+
+ g_application.m_ServiceManager->DeinitTesting();
+
+ CServiceBroker::UnregisterAppMessenger();
+
+ CAppEnvironment::TearDown();
+}
+
+void TestBasicEnvironment::SetUpError()
+{
+ fprintf(stderr, "Setup of basic environment failed.\n");
+ exit(EXIT_FAILURE);
+}
diff --git a/xbmc/test/TestBasicEnvironment.h b/xbmc/test/TestBasicEnvironment.h
new file mode 100644
index 0000000..2b88dcc
--- /dev/null
+++ b/xbmc/test/TestBasicEnvironment.h
@@ -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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+class TestBasicEnvironment : public testing::Environment
+{
+public:
+ TestBasicEnvironment();
+
+ void SetUp() override;
+ void TearDown() override;
+private:
+ void SetUpError();
+ std::string m_tempPath;
+};
diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp
new file mode 100644
index 0000000..7e51df8
--- /dev/null
+++ b/xbmc/test/TestDateTime.cpp
@@ -0,0 +1,660 @@
+/*
+ * 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 "LangInfo.h"
+#include "XBDateTime.h"
+#include "guilib/LocalizeStrings.h"
+
+#include <array>
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+class TestDateTime : public testing::Test
+{
+protected:
+ TestDateTime() = default;
+ ~TestDateTime() override = default;
+};
+
+TEST_F(TestDateTime, DateTimeOperators)
+{
+ CDateTime dateTime1(1991, 5, 14, 12, 34, 56);
+ CDateTime dateTime2(1991, 5, 14, 12, 34, 57);
+
+ EXPECT_TRUE(dateTime1 < dateTime2);
+ EXPECT_FALSE(dateTime1 > dateTime2);
+ EXPECT_FALSE(dateTime1 == dateTime2);
+}
+
+TEST_F(TestDateTime, FileTimeOperators)
+{
+ CDateTime dateTime1(1991, 5, 14, 12, 34, 56);
+ CDateTime dateTime2(1991, 5, 14, 12, 34, 57);
+
+ KODI::TIME::FileTime fileTime1;
+ KODI::TIME::FileTime fileTime2;
+
+ dateTime1.GetAsTimeStamp(fileTime1);
+ dateTime2.GetAsTimeStamp(fileTime2);
+
+ CDateTime dateTime3(fileTime1);
+
+ EXPECT_TRUE(dateTime3 < fileTime2);
+ EXPECT_FALSE(dateTime3 > fileTime2);
+ EXPECT_FALSE(dateTime3 == fileTime2);
+}
+
+TEST_F(TestDateTime, SystemTimeOperators)
+{
+ CDateTime dateTime1(1991, 5, 14, 12, 34, 56);
+ CDateTime dateTime2(1991, 5, 14, 12, 34, 57);
+
+ KODI::TIME::SystemTime systemTime;
+ dateTime2.GetAsSystemTime(systemTime);
+
+ EXPECT_TRUE(dateTime1 < systemTime);
+ EXPECT_FALSE(dateTime1 > systemTime);
+ EXPECT_FALSE(dateTime1 == systemTime);
+}
+
+TEST_F(TestDateTime, TimeTOperators)
+{
+ CDateTime dateTime1(1991, 5, 14, 12, 34, 56);
+ CDateTime dateTime2(1991, 5, 14, 12, 34, 57);
+
+ time_t time;
+ dateTime2.GetAsTime(time);
+
+ EXPECT_TRUE(dateTime1 < time);
+ EXPECT_FALSE(dateTime1 > time);
+ EXPECT_FALSE(dateTime1 == time);
+}
+
+TEST_F(TestDateTime, TmOperators)
+{
+ {
+ CDateTime dateTime1(1991, 5, 14, 12, 34, 56);
+
+ tm t1;
+ dateTime1.GetAsTm(t1);
+
+ EXPECT_FALSE(dateTime1 < t1);
+ EXPECT_FALSE(dateTime1 > t1);
+ EXPECT_TRUE(dateTime1 == t1);
+
+ CDateTime dateTime2(1991, 5, 14, 12, 34, 57);
+
+ tm t2;
+ dateTime2.GetAsTm(t2);
+
+ EXPECT_TRUE(dateTime1 < t2);
+ EXPECT_FALSE(dateTime1 > t2);
+ EXPECT_FALSE(dateTime1 == t2);
+ }
+
+ // same test but opposite daylight saving
+ {
+ CDateTime dateTime1(1991, 1, 14, 12, 34, 56);
+
+ tm t1;
+ dateTime1.GetAsTm(t1);
+
+ EXPECT_FALSE(dateTime1 < t1);
+ EXPECT_FALSE(dateTime1 > t1);
+ EXPECT_TRUE(dateTime1 == t1);
+
+ CDateTime dateTime2(1991, 1, 14, 12, 34, 57);
+
+ tm t2;
+ dateTime2.GetAsTm(t2);
+
+ EXPECT_TRUE(dateTime1 < t2);
+ EXPECT_FALSE(dateTime1 > t2);
+ EXPECT_FALSE(dateTime1 == t2);
+ }
+}
+
+// no way to test this platform agnostically (for now) so just log it.
+TEST_F(TestDateTime, GetCurrentDateTime)
+{
+ auto date = CDateTime::GetCurrentDateTime();
+ std::cout << "Current Date: " << date.GetAsDBDateTime() << std::endl;
+}
+
+// no way to test this platform agnostically (for now) so just log it.
+TEST_F(TestDateTime, GetUTCDateTime)
+{
+ auto date = CDateTime::GetUTCDateTime();
+ std::cout << "Current Date UTC: " << date.GetAsDBDateTime() << std::endl;
+}
+
+TEST_F(TestDateTime, MonthStringToMonthNum)
+{
+ std::array<std::pair<std::string, std::string>, 12> months = {{
+ {"Jan", "January"},
+ {"Feb", "February"},
+ {"Mar", "March"},
+ {"Apr", "April"},
+ {"May", "May"},
+ {"Jun", "June"},
+ {"Jul", "July"},
+ {"Aug", "August"},
+ {"Sep", "September"},
+ {"Oct", "October"},
+ {"Nov", "November"},
+ {"Dec", "December"},
+ }};
+
+ int i = 1;
+ for (const auto& month : months)
+ {
+ EXPECT_EQ(CDateTime::MonthStringToMonthNum(month.first), i);
+ EXPECT_EQ(CDateTime::MonthStringToMonthNum(month.second), i);
+ i++;
+ }
+}
+
+// this method is broken as SetFromDBDate() will return true
+TEST_F(TestDateTime, DISABLED_SetFromDateString)
+{
+ CDateTime dateTime;
+ EXPECT_TRUE(dateTime.SetFromDateString("tuesday may 14, 1991"));
+
+ std::cout << "year: " << dateTime.GetYear() << std::endl;
+ std::cout << "month: " << dateTime.GetMonth() << std::endl;
+ std::cout << "day: " << dateTime.GetDay() << std::endl;
+
+ EXPECT_EQ(dateTime.GetYear(), 1991);
+ EXPECT_EQ(dateTime.GetMonth(), 5);
+ EXPECT_EQ(dateTime.GetDay(), 14);
+}
+
+TEST_F(TestDateTime, SetFromDBDate)
+{
+ CDateTime dateTime;
+ EXPECT_TRUE(dateTime.SetFromDBDate("1991-05-14"));
+ EXPECT_EQ(dateTime.GetYear(), 1991);
+ EXPECT_EQ(dateTime.GetMonth(), 5);
+ EXPECT_EQ(dateTime.GetDay(), 14);
+
+ dateTime.Reset();
+ EXPECT_TRUE(dateTime.SetFromDBDate("02-01-1993"));
+ EXPECT_EQ(dateTime.GetYear(), 1993);
+ EXPECT_EQ(dateTime.GetMonth(), 1);
+ EXPECT_EQ(dateTime.GetDay(), 2);
+}
+
+// disabled on osx and freebsd as their mktime functions
+// don't work for dates before 1900
+#if defined(TARGET_DARWIN_OSX) || defined(TARGET_FREEBSD)
+TEST_F(TestDateTime, DISABLED_SetFromDBTime)
+#else
+TEST_F(TestDateTime, SetFromDBTime)
+#endif
+{
+ CDateTime dateTime1;
+ EXPECT_TRUE(dateTime1.SetFromDBTime("12:34"));
+ EXPECT_EQ(dateTime1.GetHour(), 12);
+ EXPECT_EQ(dateTime1.GetMinute(), 34);
+ EXPECT_EQ(dateTime1.GetSecond(), 0);
+
+ CDateTime dateTime2;
+ EXPECT_TRUE(dateTime2.SetFromDBTime("12:34:56"));
+ EXPECT_EQ(dateTime2.GetHour(), 12);
+ EXPECT_EQ(dateTime2.GetMinute(), 34);
+ EXPECT_EQ(dateTime2.GetSecond(), 56);
+}
+
+TEST_F(TestDateTime, SetFromDBDateTime)
+{
+ CDateTime dateTime;
+ EXPECT_TRUE(dateTime.SetFromDBDateTime("1991-05-14 12:34:56"));
+ EXPECT_EQ(dateTime.GetYear(), 1991);
+ EXPECT_EQ(dateTime.GetMonth(), 5);
+ EXPECT_EQ(dateTime.GetDay(), 14);
+ EXPECT_EQ(dateTime.GetHour(), 12);
+ EXPECT_EQ(dateTime.GetMinute(), 34);
+ EXPECT_EQ(dateTime.GetSecond(), 56);
+}
+
+TEST_F(TestDateTime, SetFromW3CDate)
+{
+ CDateTime dateTime;
+ EXPECT_TRUE(dateTime.SetFromW3CDate("1994-11-05T13:15:30Z"));
+ EXPECT_EQ(dateTime.GetYear(), 1994);
+ EXPECT_EQ(dateTime.GetMonth(), 11);
+ EXPECT_EQ(dateTime.GetDay(), 5);
+ EXPECT_EQ(dateTime.GetHour(), 0);
+ EXPECT_EQ(dateTime.GetMinute(), 0);
+ EXPECT_EQ(dateTime.GetSecond(), 0);
+}
+
+TEST_F(TestDateTime, SetFromW3CDateTime)
+{
+ CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+ CDateTime dateTime;
+ dateTime.SetFromDBDateTime("1994-11-05 13:15:30");
+ dateTime += bias;
+ std::string dateTimeStr = dateTime.GetAsDBDate() + "T" + dateTime.GetAsDBTime() + "Z";
+
+ CDateTime dateTime1;
+ EXPECT_TRUE(dateTime1.SetFromW3CDateTime(dateTimeStr));
+ EXPECT_EQ(dateTime1.GetYear(), 1994);
+ EXPECT_EQ(dateTime1.GetMonth(), 11);
+ EXPECT_EQ(dateTime1.GetDay(), 5);
+ EXPECT_EQ(dateTime1.GetHour(), 13);
+ EXPECT_EQ(dateTime1.GetMinute(), 15);
+ EXPECT_EQ(dateTime1.GetSecond(), 30);
+
+ CDateTime dateTime2;
+ EXPECT_TRUE(dateTime2.SetFromW3CDateTime("1994-11-05T08:15:30-05:00"));
+ EXPECT_EQ(dateTime2.GetYear(), 1994);
+ EXPECT_EQ(dateTime2.GetMonth(), 11);
+ EXPECT_EQ(dateTime2.GetDay(), 5);
+ EXPECT_EQ(dateTime2.GetHour(), 13);
+ EXPECT_EQ(dateTime2.GetMinute(), 15);
+ EXPECT_EQ(dateTime2.GetSecond(), 30);
+}
+
+TEST_F(TestDateTime, SetFromUTCDateTime)
+{
+ CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+
+ CDateTime dateTime1;
+ dateTime1.SetFromDBDateTime("1991-05-14 12:34:56");
+ dateTime1 += bias;
+
+ CDateTime dateTime2;
+ EXPECT_TRUE(dateTime2.SetFromUTCDateTime(dateTime1));
+ EXPECT_EQ(dateTime2.GetYear(), 1991);
+ EXPECT_EQ(dateTime2.GetMonth(), 5);
+ EXPECT_EQ(dateTime2.GetDay(), 14);
+ EXPECT_EQ(dateTime2.GetHour(), 12);
+ EXPECT_EQ(dateTime2.GetMinute(), 34);
+ EXPECT_EQ(dateTime2.GetSecond(), 56);
+
+ const time_t time = 674224496 + bias.GetSecondsTotal();
+
+ CDateTime dateTime3;
+ EXPECT_TRUE(dateTime3.SetFromUTCDateTime(time));
+ EXPECT_EQ(dateTime3.GetYear(), 1991);
+ EXPECT_EQ(dateTime3.GetMonth(), 5);
+ EXPECT_EQ(dateTime3.GetDay(), 14);
+ EXPECT_EQ(dateTime3.GetHour(), 12);
+ EXPECT_EQ(dateTime3.GetMinute(), 34);
+ EXPECT_EQ(dateTime3.GetSecond(), 56);
+}
+
+TEST_F(TestDateTime, SetFromRFC1123DateTime)
+{
+ std::string dateTime1("Mon, 21 Oct 2018 12:16:24 GMT");
+
+ CDateTime dateTime2;
+ EXPECT_TRUE(dateTime2.SetFromRFC1123DateTime(dateTime1));
+ EXPECT_EQ(dateTime2.GetYear(), 2018);
+ EXPECT_EQ(dateTime2.GetMonth(), 10);
+ EXPECT_EQ(dateTime2.GetDay(), 21);
+ EXPECT_EQ(dateTime2.GetHour(), 12);
+ EXPECT_EQ(dateTime2.GetMinute(), 16);
+ EXPECT_EQ(dateTime2.GetSecond(), 24);
+}
+
+TEST_F(TestDateTime, SetDateTime)
+{
+ CDateTime dateTime1;
+ EXPECT_TRUE(dateTime1.SetDateTime(1991, 05, 14, 12, 34, 56));
+ EXPECT_EQ(dateTime1.GetYear(), 1991);
+ EXPECT_EQ(dateTime1.GetMonth(), 5);
+ EXPECT_EQ(dateTime1.GetDay(), 14);
+ EXPECT_EQ(dateTime1.GetHour(), 12);
+ EXPECT_EQ(dateTime1.GetMinute(), 34);
+ EXPECT_EQ(dateTime1.GetSecond(), 56);
+
+ CDateTime dateTime2;
+ EXPECT_TRUE(dateTime2.SetDate(1991, 05, 14));
+ EXPECT_EQ(dateTime2.GetYear(), 1991);
+ EXPECT_EQ(dateTime2.GetMonth(), 5);
+ EXPECT_EQ(dateTime2.GetDay(), 14);
+ EXPECT_EQ(dateTime2.GetHour(), 0);
+ EXPECT_EQ(dateTime2.GetMinute(), 0);
+ EXPECT_EQ(dateTime2.GetSecond(), 0);
+
+// disabled on osx and freebsd as their mktime functions
+// don't work for dates before 1900
+#if !defined(TARGET_DARWIN_OSX) && !defined(TARGET_FREEBSD)
+ CDateTime dateTime3;
+ EXPECT_TRUE(dateTime3.SetTime(12, 34, 56));
+ EXPECT_EQ(dateTime3.GetYear(), 1601);
+ EXPECT_EQ(dateTime3.GetMonth(), 1);
+ EXPECT_EQ(dateTime3.GetDay(), 1);
+ EXPECT_EQ(dateTime3.GetHour(), 12);
+ EXPECT_EQ(dateTime3.GetMinute(), 34);
+ EXPECT_EQ(dateTime3.GetSecond(), 56);
+#endif
+}
+
+TEST_F(TestDateTime, GetAsStrings)
+{
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ EXPECT_EQ(dateTime.GetAsSaveString(), "19910514_123456");
+ EXPECT_EQ(dateTime.GetAsDBDateTime(), "1991-05-14 12:34:56");
+ EXPECT_EQ(dateTime.GetAsDBDate(), "1991-05-14");
+ EXPECT_EQ(dateTime.GetAsDBTime(), "12:34:56");
+ EXPECT_EQ(dateTime.GetAsW3CDate(), "1991-05-14");
+}
+
+// disabled because we have no way to validate these values
+// GetTimezoneBias() always returns a positive value so
+// there is no way to detect the direction of the offset
+TEST_F(TestDateTime, DISABLED_GetAsStringsWithBias)
+{
+ CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ CDateTime dateTimeWithBias(dateTime);
+ dateTimeWithBias += bias;
+
+ EXPECT_EQ(dateTime.GetAsRFC1123DateTime(), "Tue, 14 May 1991 20:34:56 GMT");
+ EXPECT_EQ(dateTime.GetAsW3CDateTime(false), "1991-05-14T12:34:56+08:00");
+ EXPECT_EQ(dateTime.GetAsW3CDateTime(true), "1991-05-14T20:34:56Z");
+}
+
+TEST_F(TestDateTime, GetAsLocalized)
+{
+ // short date formats using "/"
+ // "DD/MM/YYYY",
+ // "MM/DD/YYYY",
+ // "YYYY/MM/DD",
+ // "D/M/YYYY",
+ // short date formats using "-"
+ // "DD-MM-YYYY",
+ // "MM-DD-YYYY",
+ // "YYYY-MM-DD",
+ // "YYYY-M-D",
+ // short date formats using "."
+ // "DD.MM.YYYY",
+ // "DD.M.YYYY",
+ // "D.M.YYYY",
+ // "D. M. YYYY",
+ // "YYYY.MM.DD"
+
+ // "DDDD, D MMMM YYYY",
+ // "DDDD, DD MMMM YYYY",
+ // "DDDD, D. MMMM YYYY",
+ // "DDDD, DD. MMMM YYYY",
+ // "DDDD, MMMM D, YYYY",
+ // "DDDD, MMMM DD, YYYY",
+ // "DDDD D MMMM YYYY",
+ // "DDDD DD MMMM YYYY",
+ // "DDDD D. MMMM YYYY",
+ // "DDDD DD. MMMM YYYY",
+ // "D. MMMM YYYY",
+ // "DD. MMMM YYYY",
+ // "D. MMMM. YYYY",
+ // "DD. MMMM. YYYY",
+ // "YYYY. MMMM. D"
+
+ ASSERT_TRUE(g_localizeStrings.Load(g_langInfo.GetLanguagePath(), "resource.language.en_gb"));
+
+ // 24 hour clock must be set before time format
+ g_langInfo.Set24HourClock(false);
+ g_langInfo.SetTimeFormat("hh:mm:ss");
+
+ g_langInfo.SetShortDateFormat("MM/DD/YYYY");
+ g_langInfo.SetLongDateFormat("DDDD, DD MMMM YYYY");
+
+ CDateTime dateTime1;
+ dateTime1.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ // std::cout << "GetAsLocalizedDate: " << dateTime1.GetAsLocalizedDate(false) << std::endl;
+ // std::cout << "GetAsLocalizedDate: " << dateTime1.GetAsLocalizedDate(true) << std::endl;
+ // std::cout << "GetAsLocalizedDate: " << dateTime1.GetAsLocalizedDate(std::string("dd-mm-yyyy")) << std::endl;
+ // std::cout << "GetAsLocalizedTime: " << dateTime1.GetAsLocalizedTime("hh-mm-ss", true) << std::endl;
+ // std::cout << "GetAsLocalizedTime: " << dateTime1.GetAsLocalizedTime("hh-mm-ss", false)
+ // << std::endl;
+ // std::cout << "GetAsLocalizedDateTime: " << dateTime1.GetAsLocalizedDateTime(false, false)
+ // << std::endl;
+ // std::cout << "GetAsLocalizedDateTime: " << dateTime1.GetAsLocalizedDateTime(true, true)
+ // << std::endl;
+ // std::cout << "GetAsLocalizedTime: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(0), false)
+ // << std::endl;
+ // std::cout << "GetAsLocalizedTime: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(0), true)
+ // << std::endl;
+
+ // std::cout << "1: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(1)) << std::endl;
+ // std::cout << "2: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(2)) << std::endl;
+ // std::cout << "3: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(3)) << std::endl;
+ // std::cout << "4: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(4)) << std::endl;
+ // std::cout << "5: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(5)) << std::endl;
+ // std::cout << "6: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(6)) << std::endl;
+ // std::cout << "7: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(7)) << std::endl;
+ // std::cout << "8: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(8)) << std::endl;
+ // std::cout << "14: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(14)) << std::endl;
+ // std::cout << "15: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(15)) << std::endl;
+ // std::cout << "16: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(16)) << std::endl;
+ // std::cout << "19: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(19)) << std::endl;
+ // std::cout << "27: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(27)) << std::endl;
+ // std::cout << "32: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(32)) << std::endl;
+ // std::cout << "64: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(64)) << std::endl;
+ // std::cout << "128: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(128)) << std::endl;
+ // std::cout << "256: " << dateTime1.GetAsLocalizedTime(TIME_FORMAT(256)) << std::endl;
+
+ EXPECT_EQ(dateTime1.GetAsLocalizedDate(false), "05/14/1991");
+ EXPECT_EQ(dateTime1.GetAsLocalizedDate(true), "Tuesday, 14 May 1991");
+ EXPECT_EQ(dateTime1.GetAsLocalizedDate(std::string("dd-mm-yyyy")),
+ "14-05-1991"); // need to force overload function
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime("hh-mm-ss", true), "12-34-56");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime("hh-mm-ss", false), "12-34");
+ EXPECT_EQ(dateTime1.GetAsLocalizedDateTime(false, false), "05/14/1991 12:34");
+ EXPECT_EQ(dateTime1.GetAsLocalizedDateTime(true, true), "Tuesday, 14 May 1991 12:34:56");
+
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(0), false), "12:34");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(0), true), "12:34:56");
+
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(1)), "56");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(2)), "34");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(3)), "34:56");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(4)), "12");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(5)), "12:56");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(6)), "12:34");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(7)), "12:34:56");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(8)), "PM");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(14)), "12:34 PM");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(15)), "12:34:56 PM");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(16)), "12");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(19)), "12:34:56");
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(27)), "12:34:56 PM");
+
+ // not possible to use these three
+ // EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(32)), "");
+ // EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(64)), "");
+ // EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(128)), "");
+
+ EXPECT_EQ(dateTime1.GetAsLocalizedTime(TIME_FORMAT(256)), "34");
+
+
+ // 24 hour clock must be set before time format
+ g_langInfo.Set24HourClock(true);
+ g_langInfo.SetTimeFormat("h:m:s");
+
+ g_langInfo.SetShortDateFormat("YYYY-M-D");
+ g_langInfo.SetLongDateFormat("DDDD, MMMM D, YYYY");
+
+ CDateTime dateTime2;
+ dateTime2.SetDateTime(2020, 2, 3, 4, 5, 6);
+
+ // std::cout << "GetAsLocalizedDate: " << dateTime2.GetAsLocalizedDate(false) << std::endl;
+ // std::cout << "GetAsLocalizedDate: " << dateTime2.GetAsLocalizedDate(true) << std::endl;
+ // std::cout << "GetAsLocalizedDate: " << dateTime2.GetAsLocalizedDate(std::string("dd-mm-yyyy")) << std::endl;
+ // std::cout << "GetAsLocalizedTime: " << dateTime2.GetAsLocalizedTime("hh-mm-ss", true) << std::endl;
+ // std::cout << "GetAsLocalizedTime: " << dateTime2.GetAsLocalizedTime("hh-mm-ss", false)
+ // << std::endl;
+ // std::cout << "GetAsLocalizedDateTime: " << dateTime2.GetAsLocalizedDateTime(false, false)
+ // << std::endl;
+ // std::cout << "GetAsLocalizedDateTime: " << dateTime2.GetAsLocalizedDateTime(true, true)
+ // << std::endl;
+ // std::cout << "GetAsLocalizedTime: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(0), false)
+ // << std::endl;
+ // std::cout << "GetAsLocalizedTime: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(0), true)
+ // << std::endl;
+
+ // std::cout << "1: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(1)) << std::endl;
+ // std::cout << "2: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(2)) << std::endl;
+ // std::cout << "3: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(3)) << std::endl;
+ // std::cout << "4: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(4)) << std::endl;
+ // std::cout << "5: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(5)) << std::endl;
+ // std::cout << "6: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(6)) << std::endl;
+ // std::cout << "7: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(7)) << std::endl;
+ // std::cout << "8: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(8)) << std::endl;
+ // std::cout << "14: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(14)) << std::endl;
+ // std::cout << "15: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(15)) << std::endl;
+ // std::cout << "16: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(16)) << std::endl;
+ // std::cout << "19: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(19)) << std::endl;
+ // std::cout << "27: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(27)) << std::endl;
+ // std::cout << "32: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(32)) << std::endl;
+ // std::cout << "64: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(64)) << std::endl;
+ // std::cout << "128: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(128)) << std::endl;
+ // std::cout << "256: " << dateTime2.GetAsLocalizedTime(TIME_FORMAT(256)) << std::endl;
+
+ EXPECT_EQ(dateTime2.GetAsLocalizedDate(false), "2020-2-3");
+ EXPECT_EQ(dateTime2.GetAsLocalizedDate(true), "Monday, February 3, 2020");
+ EXPECT_EQ(dateTime2.GetAsLocalizedDate(std::string("dd-mm-yyyy")),
+ "03-02-2020"); // need to force overload function
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime("hh-mm-ss", true), "04-05-06");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime("hh-mm-ss", false), "04-05");
+ EXPECT_EQ(dateTime2.GetAsLocalizedDateTime(false, false), "2020-2-3 4:5");
+ EXPECT_EQ(dateTime2.GetAsLocalizedDateTime(true, true), "Monday, February 3, 2020 4:5:6");
+
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(0), false), "4:5");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(0), true), "4:5:6");
+
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(1)), "06");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(2)), "05");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(3)), "05:06");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(4)), "04");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(5)), "04:06");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(6)), "04:05");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(7)), "04:05:06");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(8)), "");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(14)), "04:05");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(15)), "04:05:06");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(16)), "4");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(19)), "4:05:06");
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(27)), "4:05:06 AM");
+
+ // not possible to use these three
+ // EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(32)), "");
+ // EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(64)), "");
+ // EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(128)), "");
+
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(256)), "5");
+}
+
+TEST_F(TestDateTime, GetAsSystemTime)
+{
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ KODI::TIME::SystemTime systemTime;
+ dateTime.GetAsSystemTime(systemTime);
+
+ EXPECT_TRUE(dateTime == systemTime);
+}
+
+TEST_F(TestDateTime, GetAsTime)
+{
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ time_t time;
+ dateTime.GetAsTime(time);
+
+ EXPECT_TRUE(dateTime == time);
+}
+
+TEST_F(TestDateTime, GetAsTm)
+{
+ {
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ tm time;
+ dateTime.GetAsTm(time);
+ EXPECT_TRUE(dateTime == time);
+ }
+
+ // same test but opposite daylight saving
+ {
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 01, 14, 12, 34, 56);
+
+ tm time;
+ dateTime.GetAsTm(time);
+ EXPECT_TRUE(dateTime == time);
+ }
+}
+
+// Disabled pending std::chrono and std::date changes.
+TEST_F(TestDateTime, DISABLED_GetAsTimeStamp)
+{
+ CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ KODI::TIME::FileTime fileTime;
+ dateTime.GetAsTimeStamp(fileTime);
+ dateTime += bias;
+
+ EXPECT_TRUE(dateTime == fileTime);
+}
+
+TEST_F(TestDateTime, GetAsUTCDateTime)
+{
+ CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+
+ CDateTime dateTime1;
+ dateTime1.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ CDateTime dateTime2;
+ dateTime2 = dateTime1.GetAsUTCDateTime();
+ dateTime2 -= bias;
+
+ EXPECT_EQ(dateTime2.GetYear(), 1991);
+ EXPECT_EQ(dateTime2.GetMonth(), 5);
+ EXPECT_EQ(dateTime2.GetDay(), 14);
+ EXPECT_EQ(dateTime2.GetHour(), 12);
+ EXPECT_EQ(dateTime2.GetMinute(), 34);
+ EXPECT_EQ(dateTime2.GetSecond(), 56);
+}
+
+// disabled on osx and freebsd as their mktime functions
+// don't work for dates before 1900
+#if defined(TARGET_DARWIN_OSX) || defined(TARGET_FREEBSD)
+TEST_F(TestDateTime, DISABLED_Reset)
+#else
+TEST_F(TestDateTime, Reset)
+#endif
+{
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ dateTime.Reset();
+
+ EXPECT_EQ(dateTime.GetYear(), 1601);
+ EXPECT_EQ(dateTime.GetMonth(), 1);
+ EXPECT_EQ(dateTime.GetDay(), 1);
+ EXPECT_EQ(dateTime.GetHour(), 0);
+ EXPECT_EQ(dateTime.GetMinute(), 0);
+ EXPECT_EQ(dateTime.GetSecond(), 0);
+}
diff --git a/xbmc/test/TestDateTimeSpan.cpp b/xbmc/test/TestDateTimeSpan.cpp
new file mode 100644
index 0000000..2ebdc59
--- /dev/null
+++ b/xbmc/test/TestDateTimeSpan.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "XBDateTime.h"
+
+#include <gtest/gtest.h>
+
+class TestDateTimeSpan : public testing::Test
+{
+protected:
+ TestDateTimeSpan() = default;
+ ~TestDateTimeSpan() override = default;
+};
+
+TEST_F(TestDateTimeSpan, Operators)
+{
+ CDateTimeSpan timeSpan1(1, 1, 1, 1);
+ CDateTimeSpan timeSpan2(2, 2, 2, 2);
+
+ EXPECT_FALSE(timeSpan1 > timeSpan2);
+ EXPECT_TRUE(timeSpan1 < timeSpan2);
+
+ CDateTimeSpan timeSpan3(timeSpan1);
+ EXPECT_TRUE(timeSpan1 == timeSpan3);
+
+ EXPECT_TRUE((timeSpan1 + timeSpan3) == timeSpan2);
+ EXPECT_TRUE((timeSpan2 - timeSpan3) == timeSpan1);
+
+ timeSpan1 += timeSpan3;
+ EXPECT_TRUE(timeSpan1 == timeSpan2);
+
+ timeSpan1 -= timeSpan3;
+ EXPECT_TRUE(timeSpan1 == timeSpan3);
+}
+
+TEST_F(TestDateTimeSpan, SetDateTimeSpan)
+{
+ CDateTimeSpan timeSpan;
+ int days = 1;
+ int hours = 2;
+ int minutes = 3;
+ int seconds = 4;
+
+ int secondsTotal = (days * 24 * 60 * 60) + (hours * 60 * 60) + (minutes * 60) + seconds;
+
+ timeSpan.SetDateTimeSpan(days, hours, minutes, seconds);
+ EXPECT_EQ(timeSpan.GetDays(), days);
+ EXPECT_EQ(timeSpan.GetHours(), hours);
+ EXPECT_EQ(timeSpan.GetMinutes(), minutes);
+ EXPECT_EQ(timeSpan.GetSeconds(), seconds);
+ EXPECT_EQ(timeSpan.GetSecondsTotal(), secondsTotal);
+}
+
+TEST_F(TestDateTimeSpan, SetFromPeriod)
+{
+ CDateTimeSpan timeSpan;
+
+ timeSpan.SetFromPeriod("3");
+ EXPECT_EQ(timeSpan.GetDays(), 3);
+
+ timeSpan.SetFromPeriod("3weeks");
+ EXPECT_EQ(timeSpan.GetDays(), 21);
+
+ timeSpan.SetFromPeriod("3months");
+ EXPECT_EQ(timeSpan.GetDays(), 93);
+}
+
+TEST_F(TestDateTimeSpan, SetFromTimeString)
+{
+ CDateTimeSpan timeSpan;
+
+ timeSpan.SetFromTimeString("12:34");
+ EXPECT_EQ(timeSpan.GetHours(), 12);
+ EXPECT_EQ(timeSpan.GetMinutes(), 34);
+}
diff --git a/xbmc/test/TestFileItem.cpp b/xbmc/test/TestFileItem.cpp
new file mode 100644
index 0000000..e78ba27
--- /dev/null
+++ b/xbmc/test/TestFileItem.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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 "URL.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/SettingsManager.h"
+
+#include <gtest/gtest.h>
+
+using ::testing::Test;
+using ::testing::WithParamInterface;
+using ::testing::ValuesIn;
+
+struct TestFileData
+{
+ const char *file;
+ bool use_folder;
+ const char *base;
+};
+
+class AdvancedSettingsResetBase : public Test
+{
+public:
+ AdvancedSettingsResetBase();
+};
+
+AdvancedSettingsResetBase::AdvancedSettingsResetBase()
+{
+ // Force all advanced settings to be reset to defaults
+ const auto settings = CServiceBroker::GetSettingsComponent();
+ CSettingsManager* settingsMgr = settings->GetSettings()->GetSettingsManager();
+ settings->GetAdvancedSettings()->Uninitialize(*settingsMgr);
+ settings->GetAdvancedSettings()->Initialize(*settingsMgr);
+}
+
+class TestFileItemSpecifiedArtJpg : public AdvancedSettingsResetBase,
+ public WithParamInterface<TestFileData>
+{
+};
+
+
+TEST_P(TestFileItemSpecifiedArtJpg, GetLocalArt)
+{
+ CFileItem item;
+ item.SetPath(GetParam().file);
+ std::string path = CURL(item.GetLocalArt("art.jpg", GetParam().use_folder)).Get();
+ std::string compare = CURL(GetParam().base).Get();
+ EXPECT_EQ(compare, path);
+}
+
+const TestFileData MovieFiles[] = {{ "c:\\dir\\filename.avi", false, "c:\\dir\\filename-art.jpg" },
+ { "c:\\dir\\filename.avi", true, "c:\\dir\\art.jpg" },
+ { "/dir/filename.avi", false, "/dir/filename-art.jpg" },
+ { "/dir/filename.avi", true, "/dir/art.jpg" },
+ { "smb://somepath/file.avi", false, "smb://somepath/file-art.jpg" },
+ { "smb://somepath/file.avi", true, "smb://somepath/art.jpg" },
+ { "stack:///path/to/movie-cd1.avi , /path/to/movie-cd2.avi", false, "/path/to/movie-art.jpg" },
+ { "stack:///path/to/movie-cd1.avi , /path/to/movie-cd2.avi", true, "/path/to/art.jpg" },
+ { "stack:///path/to/movie_name/cd1/some_file1.avi , /path/to/movie_name/cd2/some_file2.avi", true, "/path/to/movie_name/art.jpg" },
+ { "/home/user/TV Shows/Dexter/S1/1x01.avi", false, "/home/user/TV Shows/Dexter/S1/1x01-art.jpg" },
+ { "/home/user/TV Shows/Dexter/S1/1x01.avi", true, "/home/user/TV Shows/Dexter/S1/art.jpg" },
+ { "zip://g%3a%5cmultimedia%5cmovies%5cSphere%2ezip/Sphere.avi", false, "g:\\multimedia\\movies\\Sphere-art.jpg" },
+ { "zip://g%3a%5cmultimedia%5cmovies%5cSphere%2ezip/Sphere.avi", true, "g:\\multimedia\\movies\\art.jpg" },
+ { "/home/user/movies/movie_name/video_ts/VIDEO_TS.IFO", false, "/home/user/movies/movie_name/art.jpg" },
+ { "/home/user/movies/movie_name/video_ts/VIDEO_TS.IFO", true, "/home/user/movies/movie_name/art.jpg" },
+ { "/home/user/movies/movie_name/BDMV/index.bdmv", false, "/home/user/movies/movie_name/art.jpg" },
+ { "/home/user/movies/movie_name/BDMV/index.bdmv", true, "/home/user/movies/movie_name/art.jpg" }};
+
+INSTANTIATE_TEST_SUITE_P(MovieFiles, TestFileItemSpecifiedArtJpg, ValuesIn(MovieFiles));
+
+class TestFileItemFallbackArt : public AdvancedSettingsResetBase,
+ public WithParamInterface<TestFileData>
+{
+};
+
+TEST_P(TestFileItemFallbackArt, GetLocalArt)
+{
+ CFileItem item;
+ item.SetPath(GetParam().file);
+ std::string path = CURL(item.GetLocalArt("", GetParam().use_folder)).Get();
+ std::string compare = CURL(GetParam().base).Get();
+ EXPECT_EQ(compare, path);
+}
+
+const TestFileData NoArtFiles[] = {{ "c:\\dir\\filename.avi", false, "c:\\dir\\filename.tbn" },
+ { "/dir/filename.avi", false, "/dir/filename.tbn" },
+ { "smb://somepath/file.avi", false, "smb://somepath/file.tbn" },
+ { "/home/user/TV Shows/Dexter/S1/1x01.avi", false, "/home/user/TV Shows/Dexter/S1/1x01.tbn" },
+ { "zip://g%3a%5cmultimedia%5cmovies%5cSphere%2ezip/Sphere.avi", false, "g:\\multimedia\\movies\\Sphere.tbn" }};
+
+INSTANTIATE_TEST_SUITE_P(NoArt, TestFileItemFallbackArt, ValuesIn(NoArtFiles));
+
+class TestFileItemBasePath : public AdvancedSettingsResetBase,
+ public WithParamInterface<TestFileData>
+{
+};
+
+TEST_P(TestFileItemBasePath, GetBaseMoviePath)
+{
+ CFileItem item;
+ item.SetPath(GetParam().file);
+ std::string path = CURL(item.GetBaseMoviePath(GetParam().use_folder)).Get();
+ std::string compare = CURL(GetParam().base).Get();
+ EXPECT_EQ(compare, path);
+}
+
+const TestFileData BaseMovies[] = {{ "c:\\dir\\filename.avi", false, "c:\\dir\\filename.avi" },
+ { "c:\\dir\\filename.avi", true, "c:\\dir\\" },
+ { "/dir/filename.avi", false, "/dir/filename.avi" },
+ { "/dir/filename.avi", true, "/dir/" },
+ { "smb://somepath/file.avi", false, "smb://somepath/file.avi" },
+ { "smb://somepath/file.avi", true, "smb://somepath/" },
+ { "stack:///path/to/movie_name/cd1/some_file1.avi , /path/to/movie_name/cd2/some_file2.avi", false, "stack:///path/to/movie_name/cd1/some_file1.avi , /path/to/movie_name/cd2/some_file2.avi" },
+ { "stack:///path/to/movie_name/cd1/some_file1.avi , /path/to/movie_name/cd2/some_file2.avi", true, "/path/to/movie_name/" },
+ { "/home/user/TV Shows/Dexter/S1/1x01.avi", false, "/home/user/TV Shows/Dexter/S1/1x01.avi" },
+ { "/home/user/TV Shows/Dexter/S1/1x01.avi", true, "/home/user/TV Shows/Dexter/S1/" },
+ { "zip://g%3a%5cmultimedia%5cmovies%5cSphere%2ezip/Sphere.avi", true, "g:\\multimedia\\movies\\" },
+ { "/home/user/movies/movie_name/video_ts/VIDEO_TS.IFO", false, "/home/user/movies/movie_name/" },
+ { "/home/user/movies/movie_name/video_ts/VIDEO_TS.IFO", true, "/home/user/movies/movie_name/" },
+ { "/home/user/movies/movie_name/BDMV/index.bdmv", false, "/home/user/movies/movie_name/" },
+ { "/home/user/movies/movie_name/BDMV/index.bdmv", true, "/home/user/movies/movie_name/" }};
+
+INSTANTIATE_TEST_SUITE_P(BaseNameMovies, TestFileItemBasePath, ValuesIn(BaseMovies));
diff --git a/xbmc/test/TestTextureUtils.cpp b/xbmc/test/TestTextureUtils.cpp
new file mode 100644
index 0000000..a925e38
--- /dev/null
+++ b/xbmc/test/TestTextureUtils.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012-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 "TextureDatabase.h"
+#include "URL.h"
+
+#include <gtest/gtest.h>
+
+using ::testing::ValuesIn;
+
+namespace
+{
+typedef struct
+{
+ const char *in;
+ const char *type;
+ const char *options;
+ const char *out;
+} TestFiles;
+
+const TestFiles test_files[] = {{ "/path/to/image/file.jpg", "", "", "image://%2fpath%2fto%2fimage%2ffile.jpg/" },
+ { "/path/to/image/file.jpg", "", "size=thumb", "image://%2fpath%2fto%2fimage%2ffile.jpg/transform?size=thumb" },
+ { "/path/to/video/file.mkv", "video", "", "image://video@%2fpath%2fto%2fvideo%2ffile.mkv/" },
+ { "/path/to/music/file.mp3", "music", "", "image://music@%2fpath%2fto%2fmusic%2ffile.mp3/" },
+ { "image://%2fpath%2fto%2fimage%2ffile.jpg/", "", "", "image://%2fpath%2fto%2fimage%2ffile.jpg/" },
+ { "image://%2fpath%2fto%2fimage%2ffile.jpg/transform?size=thumb", "", "size=thumb", "image://%2fpath%2fto%2fimage%2ffile.jpg/transform?size=thumb" }};
+
+
+class TestTextureUtils :
+ public ::testing::TestWithParam<TestFiles>
+{
+};
+
+TEST_P(TestTextureUtils, GetWrappedImageURL)
+{
+ const TestFiles &testFiles(GetParam());
+
+ std::string expected = testFiles.out;
+ std::string out = CTextureUtils::GetWrappedImageURL(testFiles.in,
+ testFiles.type,
+ testFiles.options);
+ EXPECT_EQ(expected, out);
+}
+
+INSTANTIATE_TEST_SUITE_P(SampleFiles, TestTextureUtils, ValuesIn(test_files));
+}
diff --git a/xbmc/test/TestURL.cpp b/xbmc/test/TestURL.cpp
new file mode 100644
index 0000000..e74d3be
--- /dev/null
+++ b/xbmc/test/TestURL.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.
+ */
+
+#include "URL.h"
+
+#include <gtest/gtest.h>
+
+using ::testing::Test;
+using ::testing::WithParamInterface;
+using ::testing::ValuesIn;
+
+struct TestURLGetWithoutUserDetailsData
+{
+ std::string input;
+ std::string expected;
+ bool redact;
+};
+
+std::ostream& operator<<(std::ostream& os,
+ const TestURLGetWithoutUserDetailsData& rhs)
+{
+ return os << "(Input: " << rhs.input <<
+ "; Redact: " << (rhs.redact?"true":"false") <<
+ "; Expected: " << rhs.expected << ")";
+}
+
+class TestURLGetWithoutUserDetails : public Test,
+ public WithParamInterface<TestURLGetWithoutUserDetailsData>
+{
+};
+
+TEST_P(TestURLGetWithoutUserDetails, GetWithoutUserDetails)
+{
+ CURL input(GetParam().input);
+ std::string result = input.GetWithoutUserDetails(GetParam().redact);
+ EXPECT_EQ(result, GetParam().expected);
+}
+
+const TestURLGetWithoutUserDetailsData values[] = {
+ { std::string("smb://example.com/example"), std::string("smb://example.com/example"), false },
+ { std::string("smb://example.com/example"), std::string("smb://example.com/example"), true },
+ { std::string("smb://god:universe@example.com/example"), std::string("smb://example.com/example"), false },
+ { std::string("smb://god@example.com/example"), std::string("smb://USERNAME@example.com/example"), true },
+ { std::string("smb://god:universe@example.com/example"), std::string("smb://USERNAME:PASSWORD@example.com/example"), true },
+ { std::string("http://god:universe@example.com:8448/example|auth=digest"), std::string("http://USERNAME:PASSWORD@example.com:8448/example|auth=digest"), true },
+ { std::string("smb://fd00::1/example"), std::string("smb://fd00::1/example"), false },
+ { std::string("smb://fd00::1/example"), std::string("smb://fd00::1/example"), true },
+ { std::string("smb://[fd00::1]:8080/example"), std::string("smb://[fd00::1]:8080/example"), false },
+ { std::string("smb://[fd00::1]:8080/example"), std::string("smb://[fd00::1]:8080/example"), true },
+ { std::string("smb://god:universe@[fd00::1]:8080/example"), std::string("smb://[fd00::1]:8080/example"), false },
+ { std::string("smb://god@[fd00::1]:8080/example"), std::string("smb://USERNAME@[fd00::1]:8080/example"), true },
+ { std::string("smb://god:universe@fd00::1/example"), std::string("smb://USERNAME:PASSWORD@fd00::1/example"), true },
+ { std::string("http://god:universe@[fd00::1]:8448/example|auth=digest"), std::string("http://USERNAME:PASSWORD@[fd00::1]:8448/example|auth=digest"), true },
+ { std::string("smb://00ff:1:0000:abde::/example"), std::string("smb://00ff:1:0000:abde::/example"), true },
+ { std::string("smb://god:universe@[00ff:1:0000:abde::]:8080/example"), std::string("smb://[00ff:1:0000:abde::]:8080/example"), false },
+ { std::string("smb://god@[00ff:1:0000:abde::]:8080/example"), std::string("smb://USERNAME@[00ff:1:0000:abde::]:8080/example"), true },
+ { std::string("smb://god:universe@00ff:1:0000:abde::/example"), std::string("smb://USERNAME:PASSWORD@00ff:1:0000:abde::/example"), true },
+ { std::string("http://god:universe@[00ff:1:0000:abde::]:8448/example|auth=digest"), std::string("http://USERNAME:PASSWORD@[00ff:1:0000:abde::]:8448/example|auth=digest"), true },
+ { std::string("smb://milkyway;god:universe@example.com/example"), std::string("smb://DOMAIN;USERNAME:PASSWORD@example.com/example"), true },
+ { std::string("smb://milkyway;god@example.com/example"), std::string("smb://DOMAIN;USERNAME@example.com/example"), true },
+ { std::string("smb://milkyway;@example.com/example"), std::string("smb://example.com/example"), true },
+ { std::string("smb://milkyway;god:universe@example.com/example"), std::string("smb://example.com/example"), false },
+ { std::string("smb://milkyway;god@example.com/example"), std::string("smb://example.com/example"), false },
+ { std::string("smb://milkyway;@example.com/example"), std::string("smb://example.com/example"), false },
+};
+
+INSTANTIATE_TEST_SUITE_P(URL, TestURLGetWithoutUserDetails, ValuesIn(values));
diff --git a/xbmc/test/TestUtil.cpp b/xbmc/test/TestUtil.cpp
new file mode 100644
index 0000000..827c310
--- /dev/null
+++ b/xbmc/test/TestUtil.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "Util.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestUtil, GetQualifiedFilename)
+{
+ std::string file = "../foo";
+ CUtil::GetQualifiedFilename("smb://", file);
+ EXPECT_EQ(file, "foo");
+ file = "C:\\foo\\bar";
+ CUtil::GetQualifiedFilename("smb://", file);
+ EXPECT_EQ(file, "C:\\foo\\bar");
+ file = "../foo/./bar";
+ CUtil::GetQualifiedFilename("smb://my/path", file);
+ EXPECT_EQ(file, "smb://my/foo/bar");
+ file = "smb://foo/bar/";
+ CUtil::GetQualifiedFilename("upnp://", file);
+ EXPECT_EQ(file, "smb://foo/bar/");
+}
+
+TEST(TestUtil, MakeLegalPath)
+{
+ std::string path;
+#ifdef TARGET_WINDOWS
+ path = "C:\\foo\\bar";
+ EXPECT_EQ(CUtil::MakeLegalPath(path), "C:\\foo\\bar");
+ path = "C:\\foo:\\bar\\";
+ EXPECT_EQ(CUtil::MakeLegalPath(path), "C:\\foo_\\bar\\");
+#else
+ path = "/foo/bar/";
+ EXPECT_EQ(CUtil::MakeLegalPath(path),"/foo/bar/");
+ path = "/foo?/bar";
+ EXPECT_EQ(CUtil::MakeLegalPath(path),"/foo_/bar");
+#endif
+ path = "smb://foo/bar";
+ EXPECT_EQ(CUtil::MakeLegalPath(path), "smb://foo/bar");
+ path = "smb://foo/bar?/";
+ EXPECT_EQ(CUtil::MakeLegalPath(path), "smb://foo/bar_/");
+}
+
+TEST(TestUtil, MakeShortenPath)
+{
+ std::string result;
+ EXPECT_EQ(true, CUtil::MakeShortenPath("smb://test/string/is/long/and/very/much/so", result, 10));
+ EXPECT_EQ("smb:/../so", result);
+
+ EXPECT_EQ(true, CUtil::MakeShortenPath("smb://test/string/is/long/and/very/much/so", result, 30));
+ EXPECT_EQ("smb://../../../../../../../so", result);
+
+ EXPECT_EQ(true, CUtil::MakeShortenPath("smb://test//string/is/long/and/very//much/so", result, 30));
+ EXPECT_EQ("smb:/../../../../../so", result);
+
+ EXPECT_EQ(true, CUtil::MakeShortenPath("//test//string/is/long/and/very//much/so", result, 30));
+ EXPECT_EQ("/../../../../../so", result);
+}
+
+TEST(TestUtil, ValidatePath)
+{
+ std::string path;
+#ifdef TARGET_WINDOWS
+ path = "C:/foo/bar/";
+ EXPECT_EQ(CUtil::ValidatePath(path), "C:\\foo\\bar\\");
+ path = "C:\\\\foo\\\\bar\\";
+ EXPECT_EQ(CUtil::ValidatePath(path, true), "C:\\foo\\bar\\");
+ path = "\\\\foo\\\\bar\\";
+ EXPECT_EQ(CUtil::ValidatePath(path, true), "\\\\foo\\bar\\");
+#else
+ path = "\\foo\\bar\\";
+ EXPECT_EQ(CUtil::ValidatePath(path), "/foo/bar/");
+ path = "/foo//bar/";
+ EXPECT_EQ(CUtil::ValidatePath(path, true), "/foo/bar/");
+#endif
+ path = "smb://foo/bar/";
+ EXPECT_EQ(CUtil::ValidatePath(path), "smb://foo/bar/");
+ path = "smb://foo//bar/";
+ EXPECT_EQ(CUtil::ValidatePath(path, true), "smb://foo/bar/");
+ path = "smb:\\\\foo\\\\bar\\";
+ EXPECT_EQ(CUtil::ValidatePath(path, true), "smb://foo/bar/");
+}
diff --git a/xbmc/test/TestUtils.cpp b/xbmc/test/TestUtils.cpp
new file mode 100644
index 0000000..1289c89
--- /dev/null
+++ b/xbmc/test/TestUtils.cpp
@@ -0,0 +1,324 @@
+/*
+ * 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 "TestUtils.h"
+#include "Util.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "platform/Filesystem.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+
+#ifdef TARGET_WINDOWS
+#include <windows.h>
+#else
+#include <cstdlib>
+#include <climits>
+#include <ctime>
+#endif
+
+#include <system_error>
+
+namespace fs = KODI::PLATFORM::FILESYSTEM;
+
+class CTempFile : public XFILE::CFile
+{
+public:
+ CTempFile() = default;
+ ~CTempFile()
+ {
+ Delete();
+ }
+ bool Create(const std::string &suffix)
+ {
+ std::error_code ec;
+ m_ptempFilePath = fs::temp_file_path(suffix, ec);
+ if (ec)
+ return false;
+
+ if (m_ptempFilePath.empty())
+ return false;
+
+ OpenForWrite(m_ptempFilePath, true);
+ return true;
+ }
+ bool Delete()
+ {
+ Close();
+ return CFile::Delete(m_ptempFilePath);
+ };
+ std::string getTempFilePath() const
+ {
+ return m_ptempFilePath;
+ }
+ std::string getTempFileDirectory() const
+ {
+ return URIUtils::GetDirectory(m_ptempFilePath);
+ }
+private:
+ std::string m_ptempFilePath;
+};
+
+CXBMCTestUtils::CXBMCTestUtils()
+{
+ probability = 0.01;
+}
+
+CXBMCTestUtils &CXBMCTestUtils::Instance()
+{
+ static CXBMCTestUtils instance;
+ return instance;
+}
+
+std::string CXBMCTestUtils::ReferenceFilePath(const std::string& path)
+{
+ return CSpecialProtocol::TranslatePath(URIUtils::AddFileToFolder("special://xbmc", path));
+}
+
+bool CXBMCTestUtils::SetReferenceFileBasePath()
+{
+ std::string xbmcPath = CUtil::GetHomePath();
+ if (xbmcPath.empty())
+ return false;
+
+ /* Set xbmc, xbmcbin and home path */
+ CSpecialProtocol::SetXBMCPath(xbmcPath);
+ CSpecialProtocol::SetXBMCBinPath(xbmcPath);
+ CSpecialProtocol::SetHomePath(URIUtils::AddFileToFolder(xbmcPath, "portable_data"));
+
+ return true;
+}
+
+XFILE::CFile *CXBMCTestUtils::CreateTempFile(std::string const& suffix)
+{
+ CTempFile *f = new CTempFile();
+ if (f->Create(suffix))
+ return f;
+ delete f;
+ return NULL;
+}
+
+bool CXBMCTestUtils::DeleteTempFile(XFILE::CFile *tempfile)
+{
+ if (!tempfile)
+ return true;
+ CTempFile *f = static_cast<CTempFile*>(tempfile);
+ bool retval = f->Delete();
+ delete f;
+ return retval;
+}
+
+std::string CXBMCTestUtils::TempFilePath(XFILE::CFile const* const tempfile)
+{
+ if (!tempfile)
+ return "";
+ CTempFile const* const f = static_cast<CTempFile const*>(tempfile);
+ return f->getTempFilePath();
+}
+
+std::string CXBMCTestUtils::TempFileDirectory(XFILE::CFile const* const tempfile)
+{
+ if (!tempfile)
+ return "";
+ CTempFile const* const f = static_cast<CTempFile const*>(tempfile);
+ return f->getTempFileDirectory();
+}
+
+XFILE::CFile *CXBMCTestUtils::CreateCorruptedFile(std::string const& strFileName,
+ std::string const& suffix)
+{
+ XFILE::CFile inputfile, *tmpfile = CreateTempFile(suffix);
+ unsigned char buf[20], tmpchar;
+ ssize_t size, i;
+
+ if (tmpfile && inputfile.Open(strFileName))
+ {
+ srand(time(NULL));
+ while ((size = inputfile.Read(buf, sizeof(buf))) > 0)
+ {
+ for (i = 0; i < size; i++)
+ {
+ if ((rand() % RAND_MAX) < (probability * RAND_MAX))
+ {
+ tmpchar = buf[i];
+ do
+ {
+ buf[i] = (rand() % 256);
+ } while (buf[i] == tmpchar);
+ }
+ }
+ if (tmpfile->Write(buf, size) < 0)
+ {
+ inputfile.Close();
+ tmpfile->Close();
+ DeleteTempFile(tmpfile);
+ return NULL;
+ }
+ }
+ inputfile.Close();
+ tmpfile->Close();
+ return tmpfile;
+ }
+ delete tmpfile;
+ return NULL;
+}
+
+
+std::vector<std::string> &CXBMCTestUtils::getTestFileFactoryReadUrls()
+{
+ return TestFileFactoryReadUrls;
+}
+
+std::vector<std::string> &CXBMCTestUtils::getTestFileFactoryWriteUrls()
+{
+ return TestFileFactoryWriteUrls;
+}
+
+std::string &CXBMCTestUtils::getTestFileFactoryWriteInputFile()
+{
+ return TestFileFactoryWriteInputFile;
+}
+
+void CXBMCTestUtils::setTestFileFactoryWriteInputFile(std::string const& file)
+{
+ TestFileFactoryWriteInputFile = file;
+}
+
+std::vector<std::string> &CXBMCTestUtils::getAdvancedSettingsFiles()
+{
+ return AdvancedSettingsFiles;
+}
+
+std::vector<std::string> &CXBMCTestUtils::getGUISettingsFiles()
+{
+ return GUISettingsFiles;
+}
+
+static const char usage[] =
+"Kodi Test Suite\n"
+"Usage: kodi-test [options]\n"
+"\n"
+"The following options are recognized by the kodi-test program.\n"
+"\n"
+" --add-testfilefactory-readurl [URL]\n"
+" Add a url to be used int the TestFileFactory read tests.\n"
+"\n"
+" --add-testfilefactory-readurls [URLS]\n"
+" Add multiple urls from a ',' delimited string of urls to be used\n"
+" in the TestFileFactory read tests.\n"
+"\n"
+" --add-testfilefactory-writeurl [URL]\n"
+" Add a url to be used int the TestFileFactory write tests.\n"
+"\n"
+" --add-testfilefactory-writeurls [URLS]\n"
+" Add multiple urls from a ',' delimited string of urls to be used\n"
+" in the TestFileFactory write tests.\n"
+"\n"
+" --set-testfilefactory-writeinputfile [FILE]\n"
+" Set the path to the input file used in the TestFileFactory write tests.\n"
+"\n"
+" --add-advancedsettings-file [FILE]\n"
+" Add an advanced settings file to be loaded in test cases that use them.\n"
+"\n"
+" --add-advancedsettings-files [FILES]\n"
+" Add multiple advanced settings files from a ',' delimited string of\n"
+" files to be loaded in test cases that use them.\n"
+"\n"
+" --add-guisettings-file [FILE]\n"
+" Add a GUI settings file to be loaded in test cases that use them.\n"
+"\n"
+" --add-guisettings-files [FILES]\n"
+" Add multiple GUI settings files from a ',' delimited string of\n"
+" files to be loaded in test cases that use them.\n"
+"\n"
+" --set-probability [PROBABILITY]\n"
+" Set the probability variable used by the file corrupting functions.\n"
+" The variable should be a double type from 0.0 to 1.0. Values given\n"
+" less than 0.0 are treated as 0.0. Values greater than 1.0 are treated\n"
+" as 1.0. The default probability is 0.01.\n"
+;
+
+void CXBMCTestUtils::ParseArgs(int argc, char **argv)
+{
+ int i;
+ std::string arg;
+ for (i = 1; i < argc; i++)
+ {
+ arg = argv[i];
+ if (arg == "--add-testfilefactory-readurl")
+ {
+ TestFileFactoryReadUrls.emplace_back(argv[++i]);
+ }
+ else if (arg == "--add-testfilefactory-readurls")
+ {
+ arg = argv[++i];
+ std::vector<std::string> urls = StringUtils::Split(arg, ",");
+ for (const auto& it : urls)
+ TestFileFactoryReadUrls.push_back(it);
+ }
+ else if (arg == "--add-testfilefactory-writeurl")
+ {
+ TestFileFactoryWriteUrls.emplace_back(argv[++i]);
+ }
+ else if (arg == "--add-testfilefactory-writeurls")
+ {
+ arg = argv[++i];
+ std::vector<std::string> urls = StringUtils::Split(arg, ",");
+ for (const auto& it : urls)
+ TestFileFactoryWriteUrls.push_back(it);
+ }
+ else if (arg == "--set-testfilefactory-writeinputfile")
+ {
+ TestFileFactoryWriteInputFile = argv[++i];
+ }
+ else if (arg == "--add-advancedsettings-file")
+ {
+ AdvancedSettingsFiles.emplace_back(argv[++i]);
+ }
+ else if (arg == "--add-advancedsettings-files")
+ {
+ arg = argv[++i];
+ std::vector<std::string> urls = StringUtils::Split(arg, ",");
+ for (const auto& it : urls)
+ AdvancedSettingsFiles.push_back(it);
+ }
+ else if (arg == "--add-guisettings-file")
+ {
+ GUISettingsFiles.emplace_back(argv[++i]);
+ }
+ else if (arg == "--add-guisettings-files")
+ {
+ arg = argv[++i];
+ std::vector<std::string> urls = StringUtils::Split(arg, ",");
+ for (const auto& it : urls)
+ GUISettingsFiles.push_back(it);
+ }
+ else if (arg == "--set-probability")
+ {
+ probability = atof(argv[++i]);
+ if (probability < 0.0)
+ probability = 0.0;
+ else if (probability > 1.0)
+ probability = 1.0;
+ }
+ else
+ {
+ std::cerr << usage;
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+std::string CXBMCTestUtils::getNewLineCharacters() const
+{
+#ifdef TARGET_WINDOWS
+ return "\r\n";
+#else
+ return "\n";
+#endif
+}
diff --git a/xbmc/test/TestUtils.h b/xbmc/test/TestUtils.h
new file mode 100644
index 0000000..b0dbcf6
--- /dev/null
+++ b/xbmc/test/TestUtils.h
@@ -0,0 +1,102 @@
+/*
+ * 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 <string>
+#include <vector>
+
+namespace XFILE
+{
+ class CFile;
+}
+
+class CXBMCTestUtils
+{
+public:
+ static CXBMCTestUtils &Instance();
+
+ /* ReferenceFilePath() is used to prepend a path with the location to the
+ * xbmc-test binary. It's assumed the test suite program will only be run
+ * with xbmc-test residing in the source tree.
+ */
+ std::string ReferenceFilePath(const std::string& path);
+
+ /* Function to set the reference file base path. */
+ bool SetReferenceFileBasePath();
+
+ /* Function used in creating a temporary file. It accepts a parameter
+ * 'suffix' to append to the end of the tempfile path. The temporary
+ * file is return as a XFILE::CFile object.
+ */
+ XFILE::CFile *CreateTempFile(std::string const& suffix);
+
+ /* Function used to close and delete a temporary file previously created
+ * using CreateTempFile().
+ */
+ bool DeleteTempFile(XFILE::CFile *tempfile);
+
+ /* Function to get path of a tempfile */
+ std::string TempFilePath(XFILE::CFile const* const tempfile);
+
+ /* Get the containing directory of a tempfile */
+ std::string TempFileDirectory(XFILE::CFile const* const tempfile);
+
+ /* Functions to get variables used in the TestFileFactory tests. */
+ std::vector<std::string> &getTestFileFactoryReadUrls();
+
+ /* Function to get variables used in the TestFileFactory tests. */
+ std::vector<std::string> &getTestFileFactoryWriteUrls();
+
+ /* Function to get the input file used in the TestFileFactory.Write tests. */
+ std::string &getTestFileFactoryWriteInputFile();
+
+ /* Function to set the input file used in the TestFileFactory.Write tests */
+ void setTestFileFactoryWriteInputFile(std::string const& file);
+
+ /* Function to get advanced settings files. */
+ std::vector<std::string> &getAdvancedSettingsFiles();
+
+ /* Function to get GUI settings files. */
+ std::vector<std::string> &getGUISettingsFiles();
+
+ /* Function used in creating a corrupted file. The parameters are a URL
+ * to the original file to be corrupted and a suffix to append to the
+ * path of the newly created file. This will return a XFILE::CFile
+ * object which is itself a tempfile object which can be used with the
+ * tempfile functions of this utility class.
+ */
+ XFILE::CFile *CreateCorruptedFile(std::string const& strFileName,
+ std::string const& suffix);
+
+ /* Function to parse command line options */
+ void ParseArgs(int argc, char **argv);
+
+ /* Function to return the newline characters for this platform */
+ std::string getNewLineCharacters() const;
+private:
+ CXBMCTestUtils();
+ CXBMCTestUtils(CXBMCTestUtils const&) = delete;
+ CXBMCTestUtils& operator=(CXBMCTestUtils const&) = delete;
+
+ std::vector<std::string> TestFileFactoryReadUrls;
+ std::vector<std::string> TestFileFactoryWriteUrls;
+ std::string TestFileFactoryWriteInputFile;
+
+ std::vector<std::string> AdvancedSettingsFiles;
+ std::vector<std::string> GUISettingsFiles;
+
+ double probability;
+};
+
+#define XBMC_REF_FILE_PATH(s) CXBMCTestUtils::Instance().ReferenceFilePath(s)
+#define XBMC_CREATETEMPFILE(a) CXBMCTestUtils::Instance().CreateTempFile(a)
+#define XBMC_DELETETEMPFILE(a) CXBMCTestUtils::Instance().DeleteTempFile(a)
+#define XBMC_TEMPFILEPATH(a) CXBMCTestUtils::Instance().TempFilePath(a)
+#define XBMC_CREATECORRUPTEDFILE(a, b) \
+ CXBMCTestUtils::Instance().CreateCorruptedFile(a, b)
diff --git a/xbmc/test/xbmc-test.cpp b/xbmc/test/xbmc-test.cpp
new file mode 100644
index 0000000..faafd5a
--- /dev/null
+++ b/xbmc/test/xbmc-test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "TestBasicEnvironment.h"
+#include "TestUtils.h"
+#include "commons/ilog.h"
+#include "threads/Thread.h"
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ CXBMCTestUtils::Instance().ParseArgs(argc, argv);
+
+ if (!testing::AddGlobalTestEnvironment(new TestBasicEnvironment()))
+ {
+ fprintf(stderr, "Unable to add basic test environment.\n");
+ exit(EXIT_FAILURE);
+ }
+ int ret = RUN_ALL_TESTS();
+
+ return ret;
+}