diff options
Diffstat (limited to 'xbmc/filesystem/test')
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&%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&%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"> </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"> </td><td><a href="folder1/">folder1/</a> </td><td align="right">2019-01-01 01:01 </td><td align="right"> - </td><td> </td></tr> +<tr><td valign="top"> </td><td><a href="folder2/">folder2/</a> </td><td align="right">2019-02-02 02:02 </td><td align="right"> - </td><td> </td></tr> +<tr><td valign="top"> </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> </td></tr> +<tr><td valign="top"> </td><td><a href="sample%20&%20samplability%204.mpg">sample & samplability 4.mpg</a> </td><td align="right">2019-04-04 04:04 </td><td align="right">123K</td><td> </td></tr> +<tr><td valign="top"> </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> </td></tr> +<tr><td valign="top"> </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> </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">- </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">- </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 & 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 = '↑'; + span.setAttribute('sortdir','up'); + rows.reverse(); + } else { + span.innerHTML = '↓'; + 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 & 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&O=A">File Name</a> <a href="?C=N&O=D"> ↓ </a></th><th style="width:20%"><a href="?C=S&O=A">File Size</a> <a href="?C=S&O=D"> ↓ </a></th><th style="width:25%"><a href="?C=M&O=A">Date</a> <a href="?C=M&O=D"> ↓ </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 & samplability 4.mpg">sample & 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 Binary files differnew file mode 100644 index 0000000..b30d92e --- /dev/null +++ b/xbmc/filesystem/test/extendedlocalheader.zip diff --git a/xbmc/filesystem/test/refRARnormal.rar b/xbmc/filesystem/test/refRARnormal.rar Binary files differnew file mode 100644 index 0000000..58fe71d --- /dev/null +++ b/xbmc/filesystem/test/refRARnormal.rar diff --git a/xbmc/filesystem/test/refRARstored.rar b/xbmc/filesystem/test/refRARstored.rar Binary files differnew file mode 100644 index 0000000..1500027 --- /dev/null +++ b/xbmc/filesystem/test/refRARstored.rar 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 Binary files differnew file mode 100644 index 0000000..20841b8 --- /dev/null +++ b/xbmc/filesystem/test/reffile.txt.rar diff --git a/xbmc/filesystem/test/reffile.txt.zip b/xbmc/filesystem/test/reffile.txt.zip Binary files differnew file mode 100644 index 0000000..dd0572f --- /dev/null +++ b/xbmc/filesystem/test/reffile.txt.zip |