summaryrefslogtreecommitdiffstats
path: root/xbmc/filesystem/test
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/filesystem/test')
-rw-r--r--xbmc/filesystem/test/CMakeLists.txt15
-rw-r--r--xbmc/filesystem/test/TestDirectory.cpp56
-rw-r--r--xbmc/filesystem/test/TestFile.cpp212
-rw-r--r--xbmc/filesystem/test/TestFileFactory.cpp161
-rw-r--r--xbmc/filesystem/test/TestHTTPDirectory.cpp301
-rw-r--r--xbmc/filesystem/test/TestNfsFile.cpp84
-rw-r--r--xbmc/filesystem/test/TestZipFile.cpp211
-rw-r--r--xbmc/filesystem/test/TestZipManager.cpp30
-rw-r--r--xbmc/filesystem/test/data/httpdirectory/apache-default.html15
-rw-r--r--xbmc/filesystem/test/data/httpdirectory/apache-fancy.html15
-rw-r--r--xbmc/filesystem/test/data/httpdirectory/apache-html.html19
-rw-r--r--xbmc/filesystem/test/data/httpdirectory/basic-multiline.html16
-rw-r--r--xbmc/filesystem/test/data/httpdirectory/basic.html13
-rw-r--r--xbmc/filesystem/test/data/httpdirectory/lighttp-default.html211
-rw-r--r--xbmc/filesystem/test/data/httpdirectory/nginx-default.html11
-rw-r--r--xbmc/filesystem/test/data/httpdirectory/nginx-fancyindex.html34
-rw-r--r--xbmc/filesystem/test/extendedlocalheader.zipbin0 -> 15352 bytes
-rw-r--r--xbmc/filesystem/test/refRARnormal.rarbin0 -> 3267 bytes
-rw-r--r--xbmc/filesystem/test/refRARstored.rarbin0 -> 5430 bytes
-rw-r--r--xbmc/filesystem/test/reffile.txt25
-rw-r--r--xbmc/filesystem/test/reffile.txt.rarbin0 -> 967 bytes
-rw-r--r--xbmc/filesystem/test/reffile.txt.zipbin0 -> 1023 bytes
22 files changed, 1429 insertions, 0 deletions
diff --git a/xbmc/filesystem/test/CMakeLists.txt b/xbmc/filesystem/test/CMakeLists.txt
new file mode 100644
index 0000000..9572459
--- /dev/null
+++ b/xbmc/filesystem/test/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(SOURCES TestDirectory.cpp
+ TestFile.cpp
+ TestFileFactory.cpp
+ TestZipFile.cpp
+ TestZipManager.cpp)
+
+if(MICROHTTPD_FOUND)
+ list(APPEND SOURCES TestHTTPDirectory.cpp)
+endif()
+
+if(NFS_FOUND)
+ list(APPEND SOURCES TestNfsFile.cpp)
+endif()
+
+core_add_test_library(filesystem_test)
diff --git a/xbmc/filesystem/test/TestDirectory.cpp b/xbmc/filesystem/test/TestDirectory.cpp
new file mode 100644
index 0000000..e99408c
--- /dev/null
+++ b/xbmc/filesystem/test/TestDirectory.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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/Directory.h"
+#include "filesystem/IDirectory.h"
+#include "filesystem/SpecialProtocol.h"
+#include "test/TestUtils.h"
+#include "utils/URIUtils.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestDirectory, General)
+{
+ std::string tmppath1, tmppath2, tmppath3;
+ CFileItemList items;
+ CFileItemPtr itemptr;
+ tmppath1 = CSpecialProtocol::TranslatePath("special://temp/");
+ tmppath1 = URIUtils::AddFileToFolder(tmppath1, "TestDirectory");
+ tmppath2 = tmppath1;
+ tmppath2 = URIUtils::AddFileToFolder(tmppath2, "subdir");
+ EXPECT_TRUE(XFILE::CDirectory::Create(tmppath1));
+ EXPECT_TRUE(XFILE::CDirectory::Exists(tmppath1));
+ EXPECT_FALSE(XFILE::CDirectory::Exists(tmppath2));
+ EXPECT_TRUE(XFILE::CDirectory::Create(tmppath2));
+ EXPECT_TRUE(XFILE::CDirectory::Exists(tmppath2));
+ EXPECT_TRUE(XFILE::CDirectory::GetDirectory(tmppath1, items, "", XFILE::DIR_FLAG_DEFAULTS));
+ XFILE::CDirectory::FilterFileDirectories(items, "");
+ tmppath3 = tmppath2;
+ URIUtils::AddSlashAtEnd(tmppath3);
+ itemptr = items[0];
+ EXPECT_STREQ(tmppath3.c_str(), itemptr->GetPath().c_str());
+ EXPECT_TRUE(XFILE::CDirectory::Remove(tmppath2));
+ EXPECT_FALSE(XFILE::CDirectory::Exists(tmppath2));
+ EXPECT_TRUE(XFILE::CDirectory::Exists(tmppath1));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(tmppath1));
+ EXPECT_FALSE(XFILE::CDirectory::Exists(tmppath1));
+}
+
+TEST(TestDirectory, CreateRecursive)
+{
+ auto path1 = URIUtils::AddFileToFolder(
+ CSpecialProtocol::TranslatePath("special://temp/"),
+ "level1");
+ auto path2 = URIUtils::AddFileToFolder(path1,
+ "level2",
+ "level3");
+
+ EXPECT_TRUE(XFILE::CDirectory::Create(path2));
+ EXPECT_TRUE(XFILE::CDirectory::RemoveRecursive(path1));
+}
diff --git a/xbmc/filesystem/test/TestFile.cpp b/xbmc/filesystem/test/TestFile.cpp
new file mode 100644
index 0000000..abefe46
--- /dev/null
+++ b/xbmc/filesystem/test/TestFile.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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/File.h"
+#include "test/TestUtils.h"
+
+#include <errno.h>
+#include <string>
+
+#include <gtest/gtest.h>
+
+TEST(TestFile, Read)
+{
+ const std::string newLine = CXBMCTestUtils::Instance().getNewLineCharacters();
+ const size_t size = 1616;
+ const size_t lines = 25;
+ size_t addPerLine = newLine.length() - 1;
+ size_t realSize = size + lines * addPerLine;
+
+ const std::string firstBuf = "About" + newLine + "-----" + newLine + "XBMC is ";
+ const std::string secondBuf = "an award-winning fre";
+ const std::string thirdBuf = "ent hub for digital ";
+ const std::string fourthBuf = "rs, XBMC is a non-pr";
+ const std::string fifthBuf = "multimedia jukebox." + newLine;
+
+ XFILE::CFile file;
+ char buf[23] = {};
+
+ size_t currentPos;
+ ASSERT_TRUE(file.Open(
+ XBMC_REF_FILE_PATH("/xbmc/filesystem/test/reffile.txt")));
+ EXPECT_EQ(0, file.GetPosition());
+ EXPECT_EQ(realSize, file.GetLength());
+ EXPECT_EQ(firstBuf.length(), static_cast<size_t>(file.Read(buf, firstBuf.length())));
+ file.Flush();
+ currentPos = firstBuf.length();
+ EXPECT_EQ(currentPos, file.GetPosition());
+ EXPECT_EQ(0, memcmp(firstBuf.c_str(), buf, firstBuf.length()));
+ EXPECT_EQ(secondBuf.length(), static_cast<size_t>(file.Read(buf, secondBuf.length())));
+ currentPos += secondBuf.length();
+ EXPECT_EQ(currentPos, file.GetPosition());
+ EXPECT_EQ(0, memcmp(secondBuf.c_str(), buf, secondBuf.length()));
+ currentPos = 100 + addPerLine * 3;
+ EXPECT_EQ(currentPos, file.Seek(currentPos));
+ EXPECT_EQ(currentPos, file.GetPosition());
+ EXPECT_EQ(thirdBuf.length(), static_cast<size_t>(file.Read(buf, thirdBuf.length())));
+ file.Flush();
+ currentPos += thirdBuf.length();
+ EXPECT_EQ(currentPos, file.GetPosition());
+ EXPECT_EQ(0, memcmp(thirdBuf.c_str(), buf, thirdBuf.length()));
+ currentPos += 100 + addPerLine * 1;
+ EXPECT_EQ(currentPos, file.Seek(100 + addPerLine * 1, SEEK_CUR));
+ EXPECT_EQ(currentPos, file.GetPosition());
+ EXPECT_EQ(fourthBuf.length(), static_cast<size_t>(file.Read(buf, fourthBuf.length())));
+ file.Flush();
+ currentPos += fourthBuf.length();
+ EXPECT_EQ(currentPos, file.GetPosition());
+ EXPECT_EQ(0, memcmp(fourthBuf.c_str(), buf, fourthBuf.length()));
+ currentPos = realSize - fifthBuf.length();
+ EXPECT_EQ(currentPos, file.Seek(-(int64_t)fifthBuf.length(), SEEK_END));
+ EXPECT_EQ(currentPos, file.GetPosition());
+ EXPECT_EQ(fifthBuf.length(), static_cast<size_t>(file.Read(buf, fifthBuf.length())));
+ file.Flush();
+ currentPos += fifthBuf.length();
+ EXPECT_EQ(currentPos, file.GetPosition());
+ EXPECT_EQ(0, memcmp(fifthBuf.c_str(), buf, fifthBuf.length()));
+ currentPos += 100;
+ EXPECT_EQ(currentPos, file.Seek(100, SEEK_CUR));
+ EXPECT_EQ(currentPos, file.GetPosition());
+ currentPos = 0;
+ EXPECT_EQ(currentPos, file.Seek(currentPos, SEEK_SET));
+ EXPECT_EQ(firstBuf.length(), static_cast<size_t>(file.Read(buf, firstBuf.length())));
+ file.Flush();
+ currentPos += firstBuf.length();
+ EXPECT_EQ(currentPos, file.GetPosition());
+ EXPECT_EQ(0, memcmp(firstBuf.c_str(), buf, firstBuf.length()));
+ EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+ EXPECT_EQ(-1, file.Seek(-100, SEEK_SET));
+ file.Close();
+}
+
+TEST(TestFile, Write)
+{
+ XFILE::CFile *file;
+ const char str[] = "TestFile.Write test string\n";
+ char buf[30] = {};
+
+ ASSERT_NE(nullptr, file = XBMC_CREATETEMPFILE(""));
+ file->Close();
+ ASSERT_TRUE(file->OpenForWrite(XBMC_TEMPFILEPATH(file), true));
+ EXPECT_EQ((int)sizeof(str), file->Write(str, sizeof(str)));
+ file->Flush();
+ EXPECT_EQ((int64_t)sizeof(str), file->GetPosition());
+ file->Close();
+ ASSERT_TRUE(file->Open(XBMC_TEMPFILEPATH(file)));
+ EXPECT_EQ(0, file->GetPosition());
+ EXPECT_EQ((int64_t)sizeof(str), file->Seek(0, SEEK_END));
+ EXPECT_EQ(0, file->Seek(0, SEEK_SET));
+ EXPECT_EQ((int64_t)sizeof(str), file->GetLength());
+ EXPECT_EQ(sizeof(str), static_cast<size_t>(file->Read(buf, sizeof(buf))));
+ file->Flush();
+ EXPECT_EQ((int64_t)sizeof(str), file->GetPosition());
+ EXPECT_EQ(0, memcmp(str, buf, sizeof(str)));
+ file->Close();
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(file));
+}
+
+TEST(TestFile, Exists)
+{
+ XFILE::CFile *file;
+
+ ASSERT_NE(nullptr, file = XBMC_CREATETEMPFILE(""));
+ file->Close();
+ EXPECT_TRUE(XFILE::CFile::Exists(XBMC_TEMPFILEPATH(file)));
+ EXPECT_FALSE(XFILE::CFile::Exists(""));
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(file));
+}
+
+TEST(TestFile, Stat)
+{
+ XFILE::CFile *file;
+ struct __stat64 buffer;
+
+ ASSERT_NE(nullptr, file = XBMC_CREATETEMPFILE(""));
+ EXPECT_EQ(0, file->Stat(&buffer));
+ file->Close();
+ EXPECT_NE(0U, buffer.st_mode | _S_IFREG);
+ EXPECT_EQ(-1, XFILE::CFile::Stat("", &buffer));
+ EXPECT_EQ(ENOENT, errno);
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(file));
+}
+
+TEST(TestFile, Delete)
+{
+ XFILE::CFile *file;
+ std::string path;
+
+ ASSERT_NE(nullptr, file = XBMC_CREATETEMPFILE(""));
+ file->Close();
+ path = XBMC_TEMPFILEPATH(file);
+ EXPECT_TRUE(XFILE::CFile::Exists(path));
+ EXPECT_TRUE(XFILE::CFile::Delete(path));
+ EXPECT_FALSE(XFILE::CFile::Exists(path));
+ EXPECT_FALSE(XBMC_DELETETEMPFILE(file));
+}
+
+TEST(TestFile, Rename)
+{
+ XFILE::CFile *file1, *file2;
+ std::string path1, path2;
+
+ ASSERT_NE(nullptr, file1 = XBMC_CREATETEMPFILE(""));
+ file1->Close();
+ path1 = XBMC_TEMPFILEPATH(file1);
+ ASSERT_NE(nullptr, file2 = XBMC_CREATETEMPFILE(""));
+ file2->Close();
+ path2 = XBMC_TEMPFILEPATH(file2);
+ EXPECT_TRUE(XFILE::CFile::Delete(path1));
+ EXPECT_FALSE(XFILE::CFile::Exists(path1));
+ EXPECT_TRUE(XFILE::CFile::Exists(path2));
+ EXPECT_TRUE(XFILE::CFile::Rename(path2, path1));
+ EXPECT_TRUE(XFILE::CFile::Exists(path1));
+ EXPECT_FALSE(XFILE::CFile::Exists(path2));
+ EXPECT_TRUE(XFILE::CFile::Delete(path1));
+ EXPECT_FALSE(XBMC_DELETETEMPFILE(file1));
+ EXPECT_FALSE(XBMC_DELETETEMPFILE(file2));
+}
+
+TEST(TestFile, Copy)
+{
+ XFILE::CFile *file1, *file2;
+ std::string path1, path2;
+
+ ASSERT_NE(nullptr, file1 = XBMC_CREATETEMPFILE(""));
+ file1->Close();
+ path1 = XBMC_TEMPFILEPATH(file1);
+ ASSERT_NE(nullptr, file2 = XBMC_CREATETEMPFILE(""));
+ file2->Close();
+ path2 = XBMC_TEMPFILEPATH(file2);
+ EXPECT_TRUE(XFILE::CFile::Delete(path1));
+ EXPECT_FALSE(XFILE::CFile::Exists(path1));
+ EXPECT_TRUE(XFILE::CFile::Exists(path2));
+ EXPECT_TRUE(XFILE::CFile::Copy(path2, path1));
+ EXPECT_TRUE(XFILE::CFile::Exists(path1));
+ EXPECT_TRUE(XFILE::CFile::Exists(path2));
+ EXPECT_TRUE(XFILE::CFile::Delete(path1));
+ EXPECT_TRUE(XFILE::CFile::Delete(path2));
+ EXPECT_FALSE(XBMC_DELETETEMPFILE(file1));
+ EXPECT_FALSE(XBMC_DELETETEMPFILE(file2));
+}
+
+TEST(TestFile, SetHidden)
+{
+ XFILE::CFile *file;
+
+ ASSERT_NE(nullptr, file = XBMC_CREATETEMPFILE(""));
+ file->Close();
+ EXPECT_TRUE(XFILE::CFile::Exists(XBMC_TEMPFILEPATH(file)));
+ bool result = XFILE::CFile::SetHidden(XBMC_TEMPFILEPATH(file), true);
+#ifdef TARGET_WINDOWS
+ EXPECT_TRUE(result);
+#else
+ EXPECT_FALSE(result);
+#endif
+ EXPECT_TRUE(XFILE::CFile::Exists(XBMC_TEMPFILEPATH(file)));
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(file));
+}
diff --git a/xbmc/filesystem/test/TestFileFactory.cpp b/xbmc/filesystem/test/TestFileFactory.cpp
new file mode 100644
index 0000000..6129592
--- /dev/null
+++ b/xbmc/filesystem/test/TestFileFactory.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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 "filesystem/File.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "test/TestUtils.h"
+#include "utils/StringUtils.h"
+
+#include <gtest/gtest.h>
+
+class TestFileFactory : public testing::Test
+{
+protected:
+ TestFileFactory()
+ {
+ std::vector<std::string> advancedsettings =
+ CXBMCTestUtils::Instance().getAdvancedSettingsFiles();
+ std::vector<std::string> guisettings =
+ CXBMCTestUtils::Instance().getGUISettingsFiles();
+
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ for (const auto& it : guisettings)
+ settings->Load(it);
+
+ const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
+ for (const auto& it : advancedsettings)
+ advancedSettings->ParseSettingsFile(it);
+
+ settings->SetLoaded();
+ }
+
+ ~TestFileFactory() override
+ {
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Unload();
+ }
+};
+
+/* The tests for XFILE::CFileFactory are tested indirectly through
+ * XFILE::CFile. Since most parts of the VFS require some form of
+ * network connection, the settings and VFS URLs must be given as
+ * arguments in the main testsuite program.
+ */
+TEST_F(TestFileFactory, Read)
+{
+ XFILE::CFile file;
+ std::string str;
+ ssize_t size, i;
+ unsigned char buf[16];
+ int64_t count = 0;
+
+ std::vector<std::string> urls =
+ CXBMCTestUtils::Instance().getTestFileFactoryReadUrls();
+
+ for (const auto& url : urls)
+ {
+ std::cout << "Testing URL: " << url << std::endl;
+ ASSERT_TRUE(file.Open(url));
+ std::cout << "file.GetLength(): " <<
+ testing::PrintToString(file.GetLength()) << std::endl;
+ std::cout << "file.Seek(file.GetLength() / 2, SEEK_CUR) return value: " <<
+ testing::PrintToString(file.Seek(file.GetLength() / 2, SEEK_CUR)) << std::endl;
+ std::cout << "file.Seek(0, SEEK_END) return value: " <<
+ testing::PrintToString(file.Seek(0, SEEK_END)) << std::endl;
+ std::cout << "file.Seek(0, SEEK_SET) return value: " <<
+ testing::PrintToString(file.Seek(0, SEEK_SET)) << std::endl;
+ std::cout << "File contents:" << std::endl;
+ while ((size = file.Read(buf, sizeof(buf))) > 0)
+ {
+ str = StringUtils::Format(" {:08X}", count);
+ std::cout << str << " ";
+ count += size;
+ for (i = 0; i < size; i++)
+ {
+ str = StringUtils::Format("{:02X} ", buf[i]);
+ std::cout << str;
+ }
+ while (i++ < static_cast<ssize_t> (sizeof(buf)))
+ std::cout << " ";
+ std::cout << " [";
+ for (i = 0; i < size; i++)
+ {
+ if (buf[i] >= ' ' && buf[i] <= '~')
+ std::cout << buf[i];
+ else
+ std::cout << ".";
+ }
+ std::cout << "]" << std::endl;
+ }
+ file.Close();
+ }
+}
+
+TEST_F(TestFileFactory, Write)
+{
+ XFILE::CFile file, inputfile;
+ std::string str;
+ size_t size, i;
+ unsigned char buf[16];
+ int64_t count = 0;
+
+ str = CXBMCTestUtils::Instance().getTestFileFactoryWriteInputFile();
+ ASSERT_TRUE(inputfile.Open(str));
+
+ std::vector<std::string> urls =
+ CXBMCTestUtils::Instance().getTestFileFactoryWriteUrls();
+
+ for (const auto& url : urls)
+ {
+ std::cout << "Testing URL: " << url << std::endl;
+ std::cout << "Writing...";
+ ASSERT_TRUE(file.OpenForWrite(url, true));
+ while ((size = inputfile.Read(buf, sizeof(buf))) > 0)
+ {
+ EXPECT_GE(file.Write(buf, size), 0);
+ }
+ file.Close();
+ std::cout << "done." << std::endl;
+ std::cout << "Reading..." << std::endl;
+ ASSERT_TRUE(file.Open(url));
+ EXPECT_EQ(inputfile.GetLength(), file.GetLength());
+ std::cout << "file.Seek(file.GetLength() / 2, SEEK_CUR) return value: " <<
+ testing::PrintToString(file.Seek(file.GetLength() / 2, SEEK_CUR)) << std::endl;
+ std::cout << "file.Seek(0, SEEK_END) return value: " <<
+ testing::PrintToString(file.Seek(0, SEEK_END)) << std::endl;
+ std::cout << "file.Seek(0, SEEK_SET) return value: " <<
+ testing::PrintToString(file.Seek(0, SEEK_SET)) << std::endl;
+ std::cout << "File contents:\n";
+ while ((size = file.Read(buf, sizeof(buf))) > 0)
+ {
+ str = StringUtils::Format(" {:08X}", count);
+ std::cout << str << " ";
+ count += size;
+ for (i = 0; i < size; i++)
+ {
+ str = StringUtils::Format("{:02X} ", buf[i]);
+ std::cout << str;
+ }
+ while (i++ < sizeof(buf))
+ std::cout << " ";
+ std::cout << " [";
+ for (i = 0; i < size; i++)
+ {
+ if (buf[i] >= ' ' && buf[i] <= '~')
+ std::cout << buf[i];
+ else
+ std::cout << ".";
+ }
+ std::cout << "]" << std::endl;
+ }
+ file.Close();
+ }
+ inputfile.Close();
+}
diff --git a/xbmc/filesystem/test/TestHTTPDirectory.cpp b/xbmc/filesystem/test/TestHTTPDirectory.cpp
new file mode 100644
index 0000000..7736307
--- /dev/null
+++ b/xbmc/filesystem/test/TestHTTPDirectory.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2015-2020 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 "URL.h"
+#include "filesystem/CurlFile.h"
+#include "filesystem/HTTPDirectory.h"
+#include "network/WebServer.h"
+#include "network/httprequesthandler/HTTPVfsHandler.h"
+#include "settings/MediaSourceSettings.h"
+#include "test/TestUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/XTimeUtils.h"
+
+#include <random>
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+
+using namespace XFILE;
+
+#define WEBSERVER_HOST "localhost"
+
+#define SOURCE_PATH "xbmc/filesystem/test/data/httpdirectory/"
+
+#define TEST_FILE_APACHE_DEFAULT "apache-default.html"
+#define TEST_FILE_APACHE_FANCY "apache-fancy.html"
+#define TEST_FILE_APACHE_HTML "apache-html.html"
+#define TEST_FILE_BASIC "basic.html"
+#define TEST_FILE_BASIC_MULTILINE "basic-multiline.html"
+#define TEST_FILE_LIGHTTP_DEFAULT "lighttp-default.html"
+#define TEST_FILE_NGINX_DEFAULT "nginx-default.html"
+#define TEST_FILE_NGINX_FANCYINDEX "nginx-fancyindex.html"
+
+#define SAMPLE_ITEM_COUNT 6
+
+#define SAMPLE_ITEM_1_LABEL "folder1"
+#define SAMPLE_ITEM_2_LABEL "folder2"
+#define SAMPLE_ITEM_3_LABEL "sample3: the sampling.mpg"
+#define SAMPLE_ITEM_4_LABEL "sample & samplability 4.mpg"
+#define SAMPLE_ITEM_5_LABEL "sample5.mpg"
+#define SAMPLE_ITEM_6_LABEL "sample6.mpg"
+
+#define SAMPLE_ITEM_1_SIZE 0
+#define SAMPLE_ITEM_2_SIZE 0
+#define SAMPLE_ITEM_3_SIZE 123
+#define SAMPLE_ITEM_4_SIZE 125952 // 123K
+#define SAMPLE_ITEM_5_SIZE 128974848 // 123M
+#define SAMPLE_ITEM_6_SIZE 132070244352 // 123G
+
+// HTTPDirectory ignores the seconds component of parsed date/times
+#define SAMPLE_ITEM_1_DATETIME "2019-01-01 01:01:00"
+#define SAMPLE_ITEM_2_DATETIME "2019-02-02 02:02:00"
+#define SAMPLE_ITEM_3_DATETIME "2019-03-03 03:03:00"
+#define SAMPLE_ITEM_4_DATETIME "2019-04-04 04:04:00"
+#define SAMPLE_ITEM_5_DATETIME "2019-05-05 05:05:00"
+#define SAMPLE_ITEM_6_DATETIME "2019-06-06 06:06:00"
+
+class TestHTTPDirectory : public testing::Test
+{
+protected:
+ TestHTTPDirectory() : m_sourcePath(XBMC_REF_FILE_PATH(SOURCE_PATH))
+ {
+ std::random_device rd;
+ std::mt19937 mt(rd());
+ std::uniform_int_distribution<uint16_t> dist(49152, 65535);
+ m_webServerPort = dist(mt);
+
+ m_baseUrl = StringUtils::Format("http://" WEBSERVER_HOST ":{}", m_webServerPort);
+ }
+
+ ~TestHTTPDirectory() override = default;
+
+protected:
+ void SetUp() override
+ {
+ SetupMediaSources();
+
+ m_webServer.Start(m_webServerPort, "", "");
+ m_webServer.RegisterRequestHandler(&m_vfsHandler);
+ }
+
+ void TearDown() override
+ {
+ if (m_webServer.IsStarted())
+ m_webServer.Stop();
+
+ m_webServer.UnregisterRequestHandler(&m_vfsHandler);
+
+ TearDownMediaSources();
+ }
+
+ void SetupMediaSources()
+ {
+ CMediaSource source;
+ source.strName = "WebServer Share";
+ source.strPath = m_sourcePath;
+ source.vecPaths.push_back(m_sourcePath);
+ source.m_allowSharing = true;
+ source.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
+ source.m_iLockMode = LOCK_MODE_EVERYONE;
+ source.m_ignore = true;
+
+ CMediaSourceSettings::GetInstance().AddShare("videos", source);
+ }
+
+ void TearDownMediaSources() { CMediaSourceSettings::GetInstance().Clear(); }
+
+ std::string GetUrl(const std::string& path)
+ {
+ if (path.empty())
+ return m_baseUrl;
+
+ return URIUtils::AddFileToFolder(m_baseUrl, path);
+ }
+
+ std::string GetUrlOfTestFile(const std::string& testFile)
+ {
+ if (testFile.empty())
+ return "";
+
+ std::string path = URIUtils::AddFileToFolder(m_sourcePath, testFile);
+ path = CURL::Encode(path);
+ path = URIUtils::AddFileToFolder("vfs", path);
+
+ return GetUrl(path);
+ }
+
+ void CheckFileItemTypes(CFileItemList const& items)
+ {
+ ASSERT_EQ(items.GetObjectCount(), SAMPLE_ITEM_COUNT);
+
+ // folders
+ ASSERT_TRUE(items[0]->m_bIsFolder);
+ ASSERT_TRUE(items[1]->m_bIsFolder);
+
+ // files
+ ASSERT_FALSE(items[2]->m_bIsFolder);
+ ASSERT_FALSE(items[3]->m_bIsFolder);
+ ASSERT_FALSE(items[4]->m_bIsFolder);
+ ASSERT_FALSE(items[5]->m_bIsFolder);
+ }
+
+ void CheckFileItemLabels(CFileItemList const& items)
+ {
+ ASSERT_EQ(items.GetObjectCount(), SAMPLE_ITEM_COUNT);
+
+ ASSERT_STREQ(items[0]->GetLabel().c_str(), SAMPLE_ITEM_1_LABEL);
+ ASSERT_STREQ(items[1]->GetLabel().c_str(), SAMPLE_ITEM_2_LABEL);
+ ASSERT_STREQ(items[2]->GetLabel().c_str(), SAMPLE_ITEM_3_LABEL);
+ ASSERT_STREQ(items[3]->GetLabel().c_str(), SAMPLE_ITEM_4_LABEL);
+ ASSERT_STREQ(items[4]->GetLabel().c_str(), SAMPLE_ITEM_5_LABEL);
+ ASSERT_STREQ(items[5]->GetLabel().c_str(), SAMPLE_ITEM_6_LABEL);
+ }
+
+ void CheckFileItemDateTimes(CFileItemList const& items)
+ {
+ ASSERT_EQ(items.GetObjectCount(), SAMPLE_ITEM_COUNT);
+
+ ASSERT_STREQ(items[0]->m_dateTime.GetAsDBDateTime().c_str(), SAMPLE_ITEM_1_DATETIME);
+ ASSERT_STREQ(items[1]->m_dateTime.GetAsDBDateTime().c_str(), SAMPLE_ITEM_2_DATETIME);
+ ASSERT_STREQ(items[2]->m_dateTime.GetAsDBDateTime().c_str(), SAMPLE_ITEM_3_DATETIME);
+ ASSERT_STREQ(items[3]->m_dateTime.GetAsDBDateTime().c_str(), SAMPLE_ITEM_4_DATETIME);
+ ASSERT_STREQ(items[4]->m_dateTime.GetAsDBDateTime().c_str(), SAMPLE_ITEM_5_DATETIME);
+ ASSERT_STREQ(items[5]->m_dateTime.GetAsDBDateTime().c_str(), SAMPLE_ITEM_6_DATETIME);
+ }
+
+ void CheckFileItemSizes(CFileItemList const& items)
+ {
+ ASSERT_EQ(items.GetObjectCount(), SAMPLE_ITEM_COUNT);
+
+ // folders
+ ASSERT_EQ(items[0]->m_dwSize, SAMPLE_ITEM_1_SIZE);
+ ASSERT_EQ(items[1]->m_dwSize, SAMPLE_ITEM_2_SIZE);
+
+ // files - due to K/M/G conversions provided by some formats, allow for
+ // non-zero values that are less than or equal to the expected file size
+ ASSERT_NE(items[2]->m_dwSize, 0);
+ ASSERT_LE(items[2]->m_dwSize, SAMPLE_ITEM_3_SIZE);
+ ASSERT_NE(items[3]->m_dwSize, 0);
+ ASSERT_LE(items[3]->m_dwSize, SAMPLE_ITEM_4_SIZE);
+ ASSERT_NE(items[4]->m_dwSize, 0);
+ ASSERT_LE(items[4]->m_dwSize, SAMPLE_ITEM_5_SIZE);
+ ASSERT_NE(items[5]->m_dwSize, 0);
+ ASSERT_LE(items[5]->m_dwSize, SAMPLE_ITEM_6_SIZE);
+ }
+
+ void CheckFileItems(CFileItemList const& items)
+ {
+ CheckFileItemTypes(items);
+ CheckFileItemLabels(items);
+ }
+
+ void CheckFileItemsAndMetadata(CFileItemList const& items)
+ {
+ CheckFileItems(items);
+ CheckFileItemDateTimes(items);
+ CheckFileItemSizes(items);
+ }
+
+ CWebServer m_webServer;
+ uint16_t m_webServerPort;
+ std::string m_baseUrl;
+ std::string const m_sourcePath;
+ CHTTPVfsHandler m_vfsHandler;
+ CHTTPDirectory m_httpDirectory;
+};
+
+TEST_F(TestHTTPDirectory, IsStarted)
+{
+ ASSERT_TRUE(m_webServer.IsStarted());
+}
+
+TEST_F(TestHTTPDirectory, ApacheDefaultIndex)
+{
+ CFileItemList items;
+
+ ASSERT_TRUE(m_httpDirectory.Exists(CURL(GetUrlOfTestFile(TEST_FILE_APACHE_DEFAULT))));
+ ASSERT_TRUE(
+ m_httpDirectory.GetDirectory(CURL(GetUrlOfTestFile(TEST_FILE_APACHE_DEFAULT)), items));
+
+ CheckFileItems(items);
+}
+
+TEST_F(TestHTTPDirectory, ApacheFancyIndex)
+{
+ CFileItemList items;
+
+ ASSERT_TRUE(m_httpDirectory.Exists(CURL(GetUrlOfTestFile(TEST_FILE_APACHE_FANCY))));
+ ASSERT_TRUE(m_httpDirectory.GetDirectory(CURL(GetUrlOfTestFile(TEST_FILE_APACHE_FANCY)), items));
+
+ CheckFileItemsAndMetadata(items);
+}
+
+TEST_F(TestHTTPDirectory, ApacheHtmlIndex)
+{
+ CFileItemList items;
+
+ ASSERT_TRUE(m_httpDirectory.Exists(CURL(GetUrlOfTestFile(TEST_FILE_APACHE_HTML))));
+ ASSERT_TRUE(m_httpDirectory.GetDirectory(CURL(GetUrlOfTestFile(TEST_FILE_APACHE_HTML)), items));
+
+ CheckFileItemsAndMetadata(items);
+}
+
+TEST_F(TestHTTPDirectory, BasicIndex)
+{
+ CFileItemList items;
+
+ ASSERT_TRUE(m_httpDirectory.Exists(CURL(GetUrlOfTestFile(TEST_FILE_BASIC))));
+ ASSERT_TRUE(m_httpDirectory.GetDirectory(CURL(GetUrlOfTestFile(TEST_FILE_BASIC)), items));
+
+ CheckFileItems(items);
+}
+
+TEST_F(TestHTTPDirectory, BasicMultilineIndex)
+{
+ CFileItemList items;
+
+ ASSERT_TRUE(m_httpDirectory.Exists(CURL(GetUrlOfTestFile(TEST_FILE_BASIC_MULTILINE))));
+ ASSERT_TRUE(
+ m_httpDirectory.GetDirectory(CURL(GetUrlOfTestFile(TEST_FILE_BASIC_MULTILINE)), items));
+
+ CheckFileItems(items);
+}
+
+TEST_F(TestHTTPDirectory, LighttpDefaultIndex)
+{
+ CFileItemList items;
+
+ ASSERT_TRUE(m_httpDirectory.Exists(CURL(GetUrlOfTestFile(TEST_FILE_LIGHTTP_DEFAULT))));
+ ASSERT_TRUE(
+ m_httpDirectory.GetDirectory(CURL(GetUrlOfTestFile(TEST_FILE_LIGHTTP_DEFAULT)), items));
+
+ CheckFileItemsAndMetadata(items);
+}
+
+TEST_F(TestHTTPDirectory, NginxDefaultIndex)
+{
+ CFileItemList items;
+
+ ASSERT_TRUE(m_httpDirectory.Exists(CURL(GetUrlOfTestFile(TEST_FILE_NGINX_DEFAULT))));
+ ASSERT_TRUE(m_httpDirectory.GetDirectory(CURL(GetUrlOfTestFile(TEST_FILE_NGINX_DEFAULT)), items));
+
+ CheckFileItemsAndMetadata(items);
+}
+
+TEST_F(TestHTTPDirectory, NginxFancyIndex)
+{
+ CFileItemList items;
+
+ ASSERT_TRUE(m_httpDirectory.Exists(CURL(GetUrlOfTestFile(TEST_FILE_NGINX_FANCYINDEX))));
+ ASSERT_TRUE(m_httpDirectory.GetDirectory(CURL(GetUrlOfTestFile(TEST_FILE_NGINX_FANCYINDEX)), items));
+
+ CheckFileItemsAndMetadata(items);
+}
diff --git a/xbmc/filesystem/test/TestNfsFile.cpp b/xbmc/filesystem/test/TestNfsFile.cpp
new file mode 100644
index 0000000..69b1203
--- /dev/null
+++ b/xbmc/filesystem/test/TestNfsFile.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "filesystem/NFSFile.h"
+#include "test/TestUtils.h"
+
+#include <errno.h>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using ::testing::Test;
+using ::testing::WithParamInterface;
+using ::testing::ValuesIn;
+
+struct SplitPath
+{
+ std::string url;
+ std::string exportPath;
+ std::string relativePath;
+ bool expectedResultExport;
+ bool expectedResultPath;
+} g_TestData[] = {
+ {"nfs://192.168.0.1:2049/srv/test/tvmedia/foo.txt", "/srv/test", "//tvmedia/foo.txt", true, true},
+ {"nfs://192.168.0.1/srv/test/tv/media/foo.txt", "/srv/test/tv", "//media/foo.txt", true, true},
+ {"nfs://192.168.0.1:2049/srv/test/tvmedia", "/srv/test", "//tvmedia", true, true},
+ {"nfs://192.168.0.1:2049/srv/test/tvmedia/", "/srv/test", "//tvmedia/", true, true},
+ {"nfs://192.168.0.1:2049/srv/test/tv/media", "/srv/test/tv", "//media", true, true},
+ {"nfs://192.168.0.1:2049/srv/test/tv/media/", "/srv/test/tv", "//media/", true, true},
+ {"nfs://192.168.0.1:2049/srv/test/tv", "/srv/test/tv", "//", true, true},
+ {"nfs://192.168.0.1:2049/srv/test/", "/srv/test", "//", true, true},
+ {"nfs://192.168.0.1:2049/", "/", "//", true, true},
+ {"nfs://192.168.0.1:2049/notexported/foo.txt", "/", "//notexported/foo.txt", true, true},
+
+ {"nfs://192.168.0.1:2049/notexported/foo.txt", "/notexported", "//foo.txt", false, false},
+ };
+
+class TestNfs : public Test,
+ public WithParamInterface<SplitPath>
+{
+};
+
+class ExportList
+{
+ public:
+ std::list<std::string> data;
+
+ ExportList()
+ {
+ data.emplace_back("/srv/test");
+ data.emplace_back("/srv/test/tv");
+ data.emplace_back("/");
+ data.sort();
+ data.reverse();
+ }
+};
+
+static ExportList exportList;
+
+TEST_P(TestNfs, splitUrlIntoExportAndPath)
+{
+ CURL url(GetParam().url);
+ std::string exportPath;
+ std::string relativePath;
+ gNfsConnection.splitUrlIntoExportAndPath(url, exportPath, relativePath, exportList.data);
+
+ if (GetParam().expectedResultExport)
+ EXPECT_STREQ(GetParam().exportPath.c_str(), exportPath.c_str());
+ else
+ EXPECT_STRNE(GetParam().exportPath.c_str(), exportPath.c_str());
+
+ if (GetParam().expectedResultPath)
+ EXPECT_STREQ(GetParam().relativePath.c_str(), relativePath.c_str());
+ else
+ EXPECT_STRNE(GetParam().relativePath.c_str(), relativePath.c_str());
+}
+
+INSTANTIATE_TEST_SUITE_P(NfsFile, TestNfs, ValuesIn(g_TestData));
diff --git a/xbmc/filesystem/test/TestZipFile.cpp b/xbmc/filesystem/test/TestZipFile.cpp
new file mode 100644
index 0000000..3ff518b
--- /dev/null
+++ b/xbmc/filesystem/test/TestZipFile.cpp
@@ -0,0 +1,211 @@
+/*
+ * 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 "filesystem/Directory.h"
+#include "filesystem/File.h"
+#include "filesystem/ZipFile.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "test/TestUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+
+#include <errno.h>
+
+#include <gtest/gtest.h>
+
+class TestZipFile : public testing::Test
+{
+protected:
+ TestZipFile() = default;
+
+ ~TestZipFile() override
+ {
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Unload();
+ }
+};
+
+TEST_F(TestZipFile, Read)
+{
+ XFILE::CFile file;
+ char buf[20] = {};
+ std::string reffile, strpathinzip;
+ CFileItemList itemlist;
+
+ reffile = XBMC_REF_FILE_PATH("xbmc/filesystem/test/reffile.txt.zip");
+ CURL zipUrl = URIUtils::CreateArchivePath("zip", CURL(reffile), "");
+ ASSERT_TRUE(XFILE::CDirectory::GetDirectory(zipUrl, itemlist, "",
+ XFILE::DIR_FLAG_NO_FILE_DIRS));
+ EXPECT_GT(itemlist.Size(), 0);
+ EXPECT_FALSE(itemlist[0]->GetPath().empty());
+ strpathinzip = itemlist[0]->GetPath();
+ ASSERT_TRUE(file.Open(strpathinzip));
+ EXPECT_EQ(0, file.GetPosition());
+ EXPECT_EQ(1616, file.GetLength());
+ EXPECT_EQ(sizeof(buf), static_cast<size_t>(file.Read(buf, sizeof(buf))));
+ file.Flush();
+ EXPECT_EQ(20, file.GetPosition());
+ EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+ EXPECT_TRUE(file.ReadString(buf, sizeof(buf)));
+ EXPECT_EQ(39, file.GetPosition());
+ EXPECT_STREQ("an award-winning fr", buf);
+ EXPECT_EQ(100, file.Seek(100));
+ EXPECT_EQ(100, file.GetPosition());
+ EXPECT_EQ(sizeof(buf), static_cast<size_t>(file.Read(buf, sizeof(buf))));
+ file.Flush();
+ EXPECT_EQ(120, file.GetPosition());
+ EXPECT_TRUE(!memcmp("ent hub for digital ", buf, sizeof(buf) - 1));
+ EXPECT_EQ(220, file.Seek(100, SEEK_CUR));
+ EXPECT_EQ(220, file.GetPosition());
+ EXPECT_EQ(sizeof(buf), static_cast<size_t>(file.Read(buf, sizeof(buf))));
+ file.Flush();
+ EXPECT_EQ(240, file.GetPosition());
+ EXPECT_TRUE(!memcmp("rs, XBMC is a non-pr", buf, sizeof(buf) - 1));
+ EXPECT_EQ(1596, file.Seek(-(int64_t)sizeof(buf), SEEK_END));
+ EXPECT_EQ(1596, file.GetPosition());
+ EXPECT_EQ(sizeof(buf), static_cast<size_t>(file.Read(buf, sizeof(buf))));
+ file.Flush();
+ EXPECT_EQ(1616, file.GetPosition());
+ EXPECT_TRUE(!memcmp("multimedia jukebox.\n", buf, sizeof(buf) - 1));
+ EXPECT_EQ(-1, file.Seek(100, SEEK_CUR));
+ EXPECT_EQ(1616, file.GetPosition());
+ EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+ EXPECT_EQ(sizeof(buf), static_cast<size_t>(file.Read(buf, sizeof(buf))));
+ file.Flush();
+ EXPECT_EQ(20, file.GetPosition());
+ EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+ EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+ EXPECT_EQ(-1, file.Seek(-100, SEEK_SET));
+ file.Close();
+}
+
+TEST_F(TestZipFile, Exists)
+{
+ std::string reffile, strpathinzip;
+ CFileItemList itemlist;
+
+ reffile = XBMC_REF_FILE_PATH("xbmc/filesystem/test/reffile.txt.zip");
+ CURL zipUrl = URIUtils::CreateArchivePath("zip", CURL(reffile), "");
+ ASSERT_TRUE(XFILE::CDirectory::GetDirectory(zipUrl, itemlist, "",
+ XFILE::DIR_FLAG_NO_FILE_DIRS));
+ strpathinzip = itemlist[0]->GetPath();
+
+ EXPECT_TRUE(XFILE::CFile::Exists(strpathinzip));
+}
+
+TEST_F(TestZipFile, Stat)
+{
+ struct __stat64 buffer;
+ std::string reffile, strpathinzip;
+ CFileItemList itemlist;
+
+ reffile = XBMC_REF_FILE_PATH("xbmc/filesystem/test/reffile.txt.zip");
+ CURL zipUrl = URIUtils::CreateArchivePath("zip", CURL(reffile), "");
+ ASSERT_TRUE(XFILE::CDirectory::GetDirectory(zipUrl, itemlist, "",
+ XFILE::DIR_FLAG_NO_FILE_DIRS));
+ strpathinzip = itemlist[0]->GetPath();
+
+ EXPECT_EQ(0, XFILE::CFile::Stat(strpathinzip, &buffer));
+ EXPECT_TRUE(buffer.st_mode | _S_IFREG);
+}
+
+/* Test case to test for graceful handling of corrupted input.
+ * NOTE: The test case is considered a "success" as long as the corrupted
+ * file was successfully generated and the test case runs without a segfault.
+ */
+TEST_F(TestZipFile, CorruptedFile)
+{
+ XFILE::CFile *file;
+ char buf[16] = {};
+ std::string reffilepath, strpathinzip, str;
+ CFileItemList itemlist;
+ ssize_t size, i;
+ int64_t count = 0;
+
+ reffilepath = XBMC_REF_FILE_PATH("xbmc/filesystem/test/reffile.txt.zip");
+ ASSERT_TRUE((file = XBMC_CREATECORRUPTEDFILE(reffilepath, ".zip")) != NULL);
+ std::cout << "Reference file generated at '" << XBMC_TEMPFILEPATH(file) << "'" << std::endl;
+
+ CURL zipUrl = URIUtils::CreateArchivePath("zip", CURL(reffilepath), "");
+ if (!XFILE::CDirectory::GetDirectory(zipUrl, itemlist, "",
+ XFILE::DIR_FLAG_NO_FILE_DIRS))
+ {
+ XBMC_DELETETEMPFILE(file);
+ SUCCEED();
+ return;
+ }
+ if (itemlist.IsEmpty())
+ {
+ XBMC_DELETETEMPFILE(file);
+ SUCCEED();
+ return;
+ }
+ strpathinzip = itemlist[0]->GetPath();
+
+ if (!file->Open(strpathinzip))
+ {
+ XBMC_DELETETEMPFILE(file);
+ SUCCEED();
+ return;
+ }
+ std::cout << "file->GetLength(): " <<
+ testing::PrintToString(file->GetLength()) << std::endl;
+ std::cout << "file->Seek(file->GetLength() / 2, SEEK_CUR) return value: " <<
+ testing::PrintToString(file->Seek(file->GetLength() / 2, SEEK_CUR)) << std::endl;
+ std::cout << "file->Seek(0, SEEK_END) return value: " <<
+ testing::PrintToString(file->Seek(0, SEEK_END)) << std::endl;
+ std::cout << "file->Seek(0, SEEK_SET) return value: " <<
+ testing::PrintToString(file->Seek(0, SEEK_SET)) << std::endl;
+ std::cout << "File contents:" << std::endl;
+ while ((size = file->Read(buf, sizeof(buf))) > 0)
+ {
+ str = StringUtils::Format(" {:08X}", count);
+ std::cout << str << " ";
+ count += size;
+ for (i = 0; i < size; i++)
+ {
+ str = StringUtils::Format("{:02X} ", buf[i]);
+ std::cout << str;
+ }
+ while (i++ < static_cast<ssize_t> (sizeof(buf)))
+ std::cout << " ";
+ std::cout << " [";
+ for (i = 0; i < size; i++)
+ {
+ if (buf[i] >= ' ' && buf[i] <= '~')
+ std::cout << buf[i];
+ else
+ std::cout << ".";
+ }
+ std::cout << "]" << std::endl;
+ }
+ file->Close();
+ XBMC_DELETETEMPFILE(file);
+}
+
+TEST_F(TestZipFile, ExtendedLocalHeader)
+{
+ XFILE::CFile file;
+ ssize_t readlen;
+ char zipdata[20000]; // size of zip file is 15352 Bytes
+
+ ASSERT_TRUE(file.Open(XBMC_REF_FILE_PATH("xbmc/filesystem/test/extendedlocalheader.zip")));
+ readlen = file.Read(zipdata, sizeof(zipdata));
+ EXPECT_TRUE(readlen);
+
+ XFILE::CZipFile zipfile;
+ std::string strBuffer;
+
+ int iSize = zipfile.UnpackFromMemory(strBuffer, std::string(zipdata, readlen), false);
+ EXPECT_EQ(152774, iSize); // sum of uncompressed size of all files in zip
+ EXPECT_TRUE(strBuffer.substr(0, 6) == "<Data>");
+ file.Close();
+}
diff --git a/xbmc/filesystem/test/TestZipManager.cpp b/xbmc/filesystem/test/TestZipManager.cpp
new file mode 100644
index 0000000..ca669c5
--- /dev/null
+++ b/xbmc/filesystem/test/TestZipManager.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017-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/ZipManager.h"
+#include "utils/RegExp.h"
+
+#include <gtest/gtest.h>
+
+TEST(TestZipManager, PathTraversal)
+{
+ CRegExp pathTraversal;
+ pathTraversal.RegComp(PATH_TRAVERSAL);
+
+ ASSERT_TRUE(pathTraversal.RegFind("..") >= 0);
+ ASSERT_TRUE(pathTraversal.RegFind("../test.txt") >= 0);
+ ASSERT_TRUE(pathTraversal.RegFind("..\\test.txt") >= 0);
+ ASSERT_TRUE(pathTraversal.RegFind("test/../test.txt") >= 0);
+ ASSERT_TRUE(pathTraversal.RegFind("test\\../test.txt") >= 0);
+ ASSERT_TRUE(pathTraversal.RegFind("test\\..\\test.txt") >= 0);
+
+ ASSERT_FALSE(pathTraversal.RegFind("...") >= 0);
+ ASSERT_FALSE(pathTraversal.RegFind("..test.txt") >= 0);
+ ASSERT_FALSE(pathTraversal.RegFind("test.txt..") >= 0);
+ ASSERT_FALSE(pathTraversal.RegFind("test..test.txt") >= 0);
+}
diff --git a/xbmc/filesystem/test/data/httpdirectory/apache-default.html b/xbmc/filesystem/test/data/httpdirectory/apache-default.html
new file mode 100644
index 0000000..e29ff27
--- /dev/null
+++ b/xbmc/filesystem/test/data/httpdirectory/apache-default.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+ <head>
+ <title>Index of /</title>
+ </head>
+ <body>
+<h1>Index of /</h1>
+<ul><li><a href="folder1/"> folder1/</a></li>
+<li><a href="folder2/"> folder2/</a></li>
+<li><a href="./sample3:%20the%20sampling.mpg"> sample3: the sampling.mpg</a></li>
+<li><a href="sample%20&amp;%20samplability%204.mpg"> sample & samplability 4.mpg</a></li>
+<li><a href="sample5.mpg"> sample5.mpg</a></li>
+<li><a href="sample6.mpg"> sample6.mpg</a></li>
+</ul>
+</body></html> \ No newline at end of file
diff --git a/xbmc/filesystem/test/data/httpdirectory/apache-fancy.html b/xbmc/filesystem/test/data/httpdirectory/apache-fancy.html
new file mode 100644
index 0000000..a45d52a
--- /dev/null
+++ b/xbmc/filesystem/test/data/httpdirectory/apache-fancy.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+ <head>
+ <title>Index of /</title>
+ </head>
+ <body>
+<h1>Index of /</h1>
+<pre> <a href="?C=N;O=D;F=1">Name</a> <a href="?C=M;O=A;F=1">Last modified</a> <a href="?C=S;O=A;F=1">Size</a> <a href="?C=D;O=A;F=1">Description</a><hr> <a href="folder1/">folder1/</a> 2019-01-01 01:01 -
+ <a href="folder2/">folder2/</a> 2019-02-02 02:02 -
+ <a href="./sample3:%20the%20sampling.mpg">sample3: the sampling.mpg</a> 2019-03-03 03:03 123
+ <a href="sample%20&amp;%20samplability%204.mpg">sample & samplability 4.mpg</a> 2019-04-04 04:04 123K
+ <a href="sample5.mpg">sample5.mpg</a> 2019-05-05 05:05 123M
+ <a href="sample6.mpg">sample6.mpg</a> 2019-06-06 06:06 123G
+<hr></pre>
+</body></html>
diff --git a/xbmc/filesystem/test/data/httpdirectory/apache-html.html b/xbmc/filesystem/test/data/httpdirectory/apache-html.html
new file mode 100644
index 0000000..8e69ab4
--- /dev/null
+++ b/xbmc/filesystem/test/data/httpdirectory/apache-html.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+ <head>
+ <title>Index of /</title>
+ </head>
+ <body>
+<h1>Index of /</h1>
+ <table>
+ <tr><th valign="top">&nbsp;</th><th><a href="?C=N;O=D;F=2">Name</a></th><th><a href="?C=M;O=A;F=2">Last modified</a></th><th><a href="?C=S;O=A;F=2">Size</a></th><th><a href="?C=D;O=A;F=2">Description</a></th></tr>
+ <tr><th colspan="5"><hr></th></tr>
+<tr><td valign="top">&nbsp;</td><td><a href="folder1/">folder1/</a> </td><td align="right">2019-01-01 01:01 </td><td align="right"> - </td><td>&nbsp;</td></tr>
+<tr><td valign="top">&nbsp;</td><td><a href="folder2/">folder2/</a> </td><td align="right">2019-02-02 02:02 </td><td align="right"> - </td><td>&nbsp;</td></tr>
+<tr><td valign="top">&nbsp;</td><td><a href="./sample3:%20the%20sampling.mpg">sample3: the sampling.mpg</a> </td><td align="right">2019-03-03 03:03 </td><td align="right">123 </td><td>&nbsp;</td></tr>
+<tr><td valign="top">&nbsp;</td><td><a href="sample%20&amp;%20samplability%204.mpg">sample & samplability 4.mpg</a> </td><td align="right">2019-04-04 04:04 </td><td align="right">123K</td><td>&nbsp;</td></tr>
+<tr><td valign="top">&nbsp;</td><td><a href="sample5.mpg">sample5.mpg</a> </td><td align="right">2019-05-05 05:05 </td><td align="right">123M</td><td>&nbsp;</td></tr>
+<tr><td valign="top">&nbsp;</td><td><a href="sample6.mpg">sample6.mpg</a> </td><td align="right">2019-06-06 06:06 </td><td align="right">123G</td><td>&nbsp;</td></tr>
+ <tr><th colspan="5"><hr></th></tr>
+</table>
+</body></html>
diff --git a/xbmc/filesystem/test/data/httpdirectory/basic-multiline.html b/xbmc/filesystem/test/data/httpdirectory/basic-multiline.html
new file mode 100644
index 0000000..707a1f0
--- /dev/null
+++ b/xbmc/filesystem/test/data/httpdirectory/basic-multiline.html
@@ -0,0 +1,16 @@
+<html>
+ <head>
+ <title>Directory Listing</title>
+ </head>
+ <body>
+ <a href="folder1/">folder1/</a>
+ <a href="folder2/">
+ folder2/
+ </a>
+ <a href="./sample3:%20the%20sampling.mpg">sample3: the sampling.mpg</a>
+ <a href="sample%20&%20samplability%204.mpg">sample & samplability 4.mpg</a> <a href="sample5.mpg">sample5.mpg</a>
+ <a href="sample6.mpg">
+ sample6.mpg
+ </a>
+ </body>
+</html> \ No newline at end of file
diff --git a/xbmc/filesystem/test/data/httpdirectory/basic.html b/xbmc/filesystem/test/data/httpdirectory/basic.html
new file mode 100644
index 0000000..ce98a10
--- /dev/null
+++ b/xbmc/filesystem/test/data/httpdirectory/basic.html
@@ -0,0 +1,13 @@
+<html>
+ <head>
+ <title>Directory Listing</title>
+ </head>
+ <body>
+ <a href="folder1/">folder1/</a>
+ <a href="folder2/">folder2/</a>
+ <a href="./sample3:%20the%20sampling.mpg">sample3: the sampling.mpg</a>
+ <a href="sample%20&%20samplability%204.mpg">sample & samplability 4.mpg</a>
+ <a href="sample5.mpg">sample5.mpg</a>
+ <a href="sample6.mpg">sample6.mpg</a>
+ </body>
+</html> \ No newline at end of file
diff --git a/xbmc/filesystem/test/data/httpdirectory/lighttp-default.html b/xbmc/filesystem/test/data/httpdirectory/lighttp-default.html
new file mode 100644
index 0000000..505f477
--- /dev/null
+++ b/xbmc/filesystem/test/data/httpdirectory/lighttp-default.html
@@ -0,0 +1,211 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Index of /</title>
+<style type="text/css">
+a, a:active {text-decoration: none; color: blue;}
+a:visited {color: #48468F;}
+a:hover, a:focus {text-decoration: underline; color: red;}
+body {background-color: #F5F5F5;}
+h2 {margin-bottom: 12px;}
+table {margin-left: 12px;}
+th, td { font: 90% monospace; text-align: left;}
+th { font-weight: bold; padding-right: 14px; padding-bottom: 3px;}
+td {padding-right: 14px;}
+td.s, th.s {text-align: right;}
+div.list { background-color: white; border-top: 1px solid #646464; border-bottom: 1px solid #646464; padding-top: 10px; padding-bottom: 14px;}
+div.foot { font: 90% monospace; color: #787878; padding-top: 4px;}
+</style>
+</head>
+<body>
+<h2>Index of /</h2>
+<div class="list">
+<table summary="Directory Listing" cellpadding="0" cellspacing="0">
+<thead><tr><th class="n">Name</th><th class="m">Last Modified</th><th class="s">Size</th><th class="t">Type</th></tr></thead>
+<tbody>
+<tr class="d"><td class="n"><a href="folder1/">folder1</a>/</td><td class="m">2019-Jan-01 01:01:01</td><td class="s">- &nbsp;</td><td class="t">Directory</td></tr>
+<tr class="d"><td class="n"><a href="folder2/">folder2</a>/</td><td class="m">2019-Feb-02 02:02:02</td><td class="s">- &nbsp;</td><td class="t">Directory</td></tr>
+<tr><td class="n"><a href="sample3%3a%20the%20sampling.mpg">sample3: the sampling.mpg</a></td><td class="m">2019-Mar-03 03:03:03</td><td class="s">0.1K</td><td class="t">video/mpeg</td></tr>
+<tr><td class="n"><a href="sample%20%26%20samplability%204.mpg">sample &#x26; samplability 4.mpg</a></td><td class="m">2019-Apr-04 04:04:04</td><td class="s">123.0K</td><td class="t">video/mpeg</td></tr>
+<tr><td class="n"><a href="sample5.mpg">sample5.mpg</a></td><td class="m">2019-May-05 05:05:05</td><td class="s">123.0M</td><td class="t">video/mpeg</td></tr>
+<tr><td class="n"><a href="sample6.mpg">sample6.mpg</a></td><td class="m">2019-Jun-06 06:06:06</td><td class="s">123.0G</td><td class="t">video/mpeg</td></tr>
+</tbody>
+</table>
+</div>
+<div class="foot">lighttpd/1.4.49</div>
+
+<script type="text/javascript">
+// <!--
+
+var click_column;
+var name_column = 0;
+var date_column = 1;
+var size_column = 2;
+var type_column = 3;
+var prev_span = null;
+
+if (typeof(String.prototype.localeCompare) === 'undefined') {
+ String.prototype.localeCompare = function(str, locale, options) {
+ return ((this == str) ? 0 : ((this > str) ? 1 : -1));
+ };
+}
+
+if (typeof(String.prototype.toLocaleUpperCase) === 'undefined') {
+ String.prototype.toLocaleUpperCase = function() {
+ return this.toUpperCase();
+ };
+}
+
+function get_inner_text(el) {
+ if((typeof el == 'string')||(typeof el == 'undefined'))
+ return el;
+ if(el.innerText)
+ return el.innerText;
+ else {
+ var str = "";
+ var cs = el.childNodes;
+ var l = cs.length;
+ for (i=0;i<l;i++) {
+ if (cs[i].nodeType==1) str += get_inner_text(cs[i]);
+ else if (cs[i].nodeType==3) str += cs[i].nodeValue;
+ }
+ }
+ return str;
+}
+
+function isdigit(c) {
+ return (c >= '0' && c <= '9');
+}
+
+function unit_multiplier(unit) {
+ return (unit=='K') ? 1000
+ : (unit=='M') ? 1000000
+ : (unit=='G') ? 1000000000
+ : (unit=='T') ? 1000000000000
+ : (unit=='P') ? 1000000000000000
+ : (unit=='E') ? 1000000000000000000 : 1;
+}
+
+var li_date_regex=/(\d{4})-(\w{3})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/;
+
+var li_mon = ['Jan','Feb','Mar','Apr','May','Jun',
+ 'Jul','Aug','Sep','Oct','Nov','Dec'];
+
+function li_mon_num(mon) {
+ var i; for (i = 0; i < 12 && mon != li_mon[i]; ++i); return i;
+}
+
+function li_date_cmp(s1, s2) {
+ var dp1 = li_date_regex.exec(s1)
+ var dp2 = li_date_regex.exec(s2)
+ for (var i = 1; i < 7; ++i) {
+ var cmp = (2 != i)
+ ? parseInt(dp1[i]) - parseInt(dp2[i])
+ : li_mon_num(dp1[2]) - li_mon_num(dp2[2]);
+ if (0 != cmp) return cmp;
+ }
+ return 0;
+}
+
+function sortfn_then_by_name(a,b,sort_column) {
+ if (sort_column == name_column || sort_column == type_column) {
+ var ad = (a.cells[type_column].innerHTML === 'Directory');
+ var bd = (b.cells[type_column].innerHTML === 'Directory');
+ if (ad != bd) return (ad ? -1 : 1);
+ }
+ var at = get_inner_text(a.cells[sort_column]);
+ var bt = get_inner_text(b.cells[sort_column]);
+ var cmp;
+ if (sort_column == name_column) {
+ if (at == '..') return -1;
+ if (bt == '..') return 1;
+ }
+ if (a.cells[sort_column].className == 'int') {
+ cmp = parseInt(at)-parseInt(bt);
+ } else if (sort_column == date_column) {
+ var ad = isdigit(at.substr(0,1));
+ var bd = isdigit(bt.substr(0,1));
+ if (ad != bd) return (!ad ? -1 : 1);
+ cmp = li_date_cmp(at,bt);
+ } else if (sort_column == size_column) {
+ var ai = parseInt(at, 10) * unit_multiplier(at.substr(-1,1));
+ var bi = parseInt(bt, 10) * unit_multiplier(bt.substr(-1,1));
+ if (at.substr(0,1) == '-') ai = -1;
+ if (bt.substr(0,1) == '-') bi = -1;
+ cmp = ai - bi;
+ } else {
+ cmp = at.toLocaleUpperCase().localeCompare(bt.toLocaleUpperCase());
+ if (0 != cmp) return cmp;
+ cmp = at.localeCompare(bt);
+ }
+ if (0 != cmp || sort_column == name_column) return cmp;
+ return sortfn_then_by_name(a,b,name_column);
+}
+
+function sortfn(a,b) {
+ return sortfn_then_by_name(a,b,click_column);
+}
+
+function resort(lnk) {
+ var span = lnk.childNodes[1];
+ var table = lnk.parentNode.parentNode.parentNode.parentNode;
+ var rows = new Array();
+ for (j=1;j<table.rows.length;j++)
+ rows[j-1] = table.rows[j];
+ click_column = lnk.parentNode.cellIndex;
+ rows.sort(sortfn);
+
+ if (prev_span != null) prev_span.innerHTML = '';
+ if (span.getAttribute('sortdir')=='down') {
+ span.innerHTML = '&uarr;';
+ span.setAttribute('sortdir','up');
+ rows.reverse();
+ } else {
+ span.innerHTML = '&darr;';
+ span.setAttribute('sortdir','down');
+ }
+ for (i=0;i<rows.length;i++)
+ table.tBodies[0].appendChild(rows[i]);
+ prev_span = span;
+}
+
+function init_sort(init_sort_column, ascending) {
+ var tables = document.getElementsByTagName("table");
+ for (var i = 0; i < tables.length; i++) {
+ var table = tables[i];
+ //var c = table.getAttribute("class")
+ //if (-1 != c.split(" ").indexOf("sort")) {
+ var row = table.rows[0].cells;
+ for (var j = 0; j < row.length; j++) {
+ var n = row[j];
+ if (n.childNodes.length == 1 && n.childNodes[0].nodeType == 3) {
+ var link = document.createElement("a");
+ var title = n.childNodes[0].nodeValue.replace(/:$/, "");
+ link.appendChild(document.createTextNode(title));
+ link.setAttribute("href", "#");
+ link.setAttribute("class", "sortheader");
+ link.setAttribute("onclick", "resort(this);return false;");
+ var arrow = document.createElement("span");
+ arrow.setAttribute("class", "sortarrow");
+ arrow.appendChild(document.createTextNode(":"));
+ link.appendChild(arrow)
+ n.replaceChild(link, n.firstChild);
+ }
+ }
+ var lnk = row[init_sort_column].firstChild;
+ if (ascending) {
+ var span = lnk.childNodes[1];
+ span.setAttribute('sortdir','down');
+ }
+ resort(lnk);
+ //}
+ }
+}
+
+init_sort(0, 0);
+
+// -->
+</script>
+
+</body>
+</html>
diff --git a/xbmc/filesystem/test/data/httpdirectory/nginx-default.html b/xbmc/filesystem/test/data/httpdirectory/nginx-default.html
new file mode 100644
index 0000000..77bcd75
--- /dev/null
+++ b/xbmc/filesystem/test/data/httpdirectory/nginx-default.html
@@ -0,0 +1,11 @@
+<html>
+<head><title>Index of /</title></head>
+<body>
+<h1>Index of /</h1><hr><pre><a href="folder1/">folder1/</a> 01-Jan-2019 01:01 -
+<a href="folder2/">folder2/</a> 02-Feb-2019 02:02 -
+<a href="sample3%3A%20the%20sampling.mpg">sample3: the sampling.mpg</a> 03-Mar-2019 03:03 123
+<a href="sample%20%26%20samplability%204.mpg">sample &#x26; samplability 4.mpg</a> 04-Apr-2019 04:04 125952
+<a href="sample5.mpg">sample5.mpg</a> 05-May-2019 05:05 128974848
+<a href="sample6.mpg">sample6.mpg</a> 06-Jun-2019 06:06 132070244352
+</pre><hr></body>
+</html>
diff --git a/xbmc/filesystem/test/data/httpdirectory/nginx-fancyindex.html b/xbmc/filesystem/test/data/httpdirectory/nginx-fancyindex.html
new file mode 100644
index 0000000..d9772df
--- /dev/null
+++ b/xbmc/filesystem/test/data/httpdirectory/nginx-fancyindex.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
+ <meta name="viewport" content="width=device-width"/>
+ <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
+ <title>Files...</title>
+</head>
+<body>
+<div class="box box-breadcrumbs">
+ <div class="box-header">
+ <div class="box-header-content">
+ <div id="breadcrumbs" class="breadcrumbs">
+ <a href="#"></a>
+ </div>
+ </div>
+ </div>
+ <div class="box-content clearfix">
+ <h1>Index of:
+/</h1>
+<table id="list"><thead><tr><th style="width:55%"><a href="?C=N&amp;O=A">File Name</a>&nbsp;<a href="?C=N&amp;O=D">&nbsp;&darr;&nbsp;</a></th><th style="width:20%"><a href="?C=S&amp;O=A">File Size</a>&nbsp;<a href="?C=S&amp;O=D">&nbsp;&darr;&nbsp;</a></th><th style="width:25%"><a href="?C=M&amp;O=A">Date</a>&nbsp;<a href="?C=M&amp;O=D">&nbsp;&darr;&nbsp;</a></th></tr></thead>
+<tbody><tr><td class="link"><a href="../">Parent directory/</a></td><td class="size">-</td><td class="date">-</td></tr>
+<tr><td class="link"><a href="folder1/" title="folder1">folder1/</a></td><td class="size">-</td><td class="date">2019-Jan-01 01:01</td></tr>
+<tr><td class="link"><a href="folder2/" title="folder2">folder2/</a></td><td class="size">-</td><td class="date">2019-Feb-02 02:02</td></tr>
+<tr><td class="link"><a href="sample3%3A%20the%20sampling.mpg" title="sample3: the sampling.mpg">sample3: the sampling.mpg</a></td><td class="size">123 B</td><td class="date">2019-Mar-03 03:03</td></tr>
+<tr><td class="link"><a href="sample%20%26%20samplability%204.mpg" title="sample &#x26; samplability 4.mpg">sample &#x26; samplability 4.mpg</a></td><td class="size">123.0 KiB</td><td class="date">2019-Apr-04 04:04</td></tr>
+<tr><td class="link"><a href="sample5.mpg" title="sample5.mpg">sample5.mpg</a></td><td class="size">123.0 MiB</td><td class="date">2019-May-05 05:05</td></tr>
+<tr><td class="link"><a href="sample6.mpg" title="sample6.mpg">sample6.mpg</a></td><td class="size">123.0 GiB</td><td class="date">2019-Jun-06 06:06</td></tr>
+</tbody></table>
+</div>
+</div>
+</body>
+</html>
diff --git a/xbmc/filesystem/test/extendedlocalheader.zip b/xbmc/filesystem/test/extendedlocalheader.zip
new file mode 100644
index 0000000..b30d92e
--- /dev/null
+++ b/xbmc/filesystem/test/extendedlocalheader.zip
Binary files differ
diff --git a/xbmc/filesystem/test/refRARnormal.rar b/xbmc/filesystem/test/refRARnormal.rar
new file mode 100644
index 0000000..58fe71d
--- /dev/null
+++ b/xbmc/filesystem/test/refRARnormal.rar
Binary files differ
diff --git a/xbmc/filesystem/test/refRARstored.rar b/xbmc/filesystem/test/refRARstored.rar
new file mode 100644
index 0000000..1500027
--- /dev/null
+++ b/xbmc/filesystem/test/refRARstored.rar
Binary files differ
diff --git a/xbmc/filesystem/test/reffile.txt b/xbmc/filesystem/test/reffile.txt
new file mode 100644
index 0000000..7a5e510
--- /dev/null
+++ b/xbmc/filesystem/test/reffile.txt
@@ -0,0 +1,25 @@
+About
+-----
+XBMC is an award-winning free and open source (GPL) software media player and
+entertainment hub for digital media. XBMC is available for multiple platforms.
+Created in 2003 by a group of like minded programmers, XBMC is a non-profit
+project run and developed by volunteers located around the world. More than 50
+software developers have contributed to XBMC, and 100-plus translators have
+worked to expand its reach, making it available in more than 30 languages.
+
+While XBMC functions very well as a standard media player application for your
+computer, it has been designed to be the perfect companion for your HTPC.
+Supporting an almost endless range of remote controls, and combined with its
+beautiful interface and powerful skinning engine, XBMC feels very natural to
+use from the couch and is the ideal solution for your home theater.
+
+Currently XBMC can be used to play almost all popular audio and video formats
+around. It was designed for network playback, so you can stream your multimedia
+from anywhere in the house or directly from the internet using practically any
+protocol available. Use your media as-is: XBMC can play CDs and DVDs directly
+from the disk or image file, almost all popular archive formats from your hard
+drive, and even files inside ZIP and RAR archives. It will even scan all of
+your media and automatically create a personalized library complete with box
+covers, descriptions, and fanart. There are playlist and slideshow functions, a
+weather forecast feature and many audio visualizations. Once installed, your
+computer will become a fully functional multimedia jukebox.
diff --git a/xbmc/filesystem/test/reffile.txt.rar b/xbmc/filesystem/test/reffile.txt.rar
new file mode 100644
index 0000000..20841b8
--- /dev/null
+++ b/xbmc/filesystem/test/reffile.txt.rar
Binary files differ
diff --git a/xbmc/filesystem/test/reffile.txt.zip b/xbmc/filesystem/test/reffile.txt.zip
new file mode 100644
index 0000000..dd0572f
--- /dev/null
+++ b/xbmc/filesystem/test/reffile.txt.zip
Binary files differ