blob: d40cccda81a5ac1091173e85f919eb89d348dd7b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <fstream>
#include <gtest/gtest.h>
#include <stdlib.h>
#include <string>
#include <thread>
#include <unistd.h>
#include <vector>
#include <zip.h>
#include "celix_file_utils.h"
#include "celix_properties.h"
#include "celix_utils.h"
class FileUtilsTestSuite : public ::testing::Test {};
TEST_F(FileUtilsTestSuite, TestFileAndDirectoryExists) {
EXPECT_TRUE(celix_utils_fileExists(TEST_A_DIR_LOCATION));
EXPECT_TRUE(celix_utils_fileExists(TEST_A_FILE_LOCATION));
EXPECT_FALSE(celix_utils_fileExists("/file-does-not-exists/check"));
EXPECT_TRUE(celix_utils_directoryExists(TEST_A_DIR_LOCATION));
EXPECT_FALSE(celix_utils_directoryExists(TEST_A_FILE_LOCATION));
EXPECT_FALSE(celix_utils_directoryExists("/file-does-not-exists/check"));
}
TEST_F(FileUtilsTestSuite, CreateAndDeleteDirectory) {
const char* testDir = "celix_file_utils_test/directory";
const char* testDir2 = "celix_file_utils_test/directory2/";
const char* testDir3 = "";//invalid
celix_utils_deleteDirectory(testDir, nullptr);
celix_utils_deleteDirectory(testDir2, nullptr);
//I can create a new directory.
const char* error = nullptr;
EXPECT_FALSE(celix_utils_directoryExists(testDir));
auto status = celix_utils_createDirectory(testDir, true, &error);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_EQ(error, nullptr);
EXPECT_TRUE(celix_utils_fileExists(testDir));
EXPECT_TRUE(celix_utils_directoryExists(testDir));
//Creating a directory if it already exists fails when using failIfPresent=true.
status = celix_utils_createDirectory(testDir, true, &error);
EXPECT_NE(status, CELIX_SUCCESS);
EXPECT_NE(error, nullptr);
status = celix_utils_createDirectory(testDir, true, nullptr);
EXPECT_NE(status, CELIX_SUCCESS);
//Creating a directory if it already exists succeeds when using failIfPresent=false.
status = celix_utils_createDirectory(testDir, false, &error);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_EQ(error, nullptr);
//Can I delete dir and when deleting a non existing dir no error is given
status = celix_utils_deleteDirectory(testDir, &error);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_EQ(error, nullptr);
status = celix_utils_deleteDirectory(testDir, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
//Can I create and delete a dir with ends with a /
status = celix_utils_createDirectory(testDir2, true, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_TRUE(celix_utils_fileExists(testDir2));
status = celix_utils_deleteDirectory(testDir2, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
status = celix_utils_createDirectory(testDir3, true, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
status = celix_utils_deleteDirectory(testDir3, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
//Can I create and delete a dir that begins with a /
auto cwd = getcwd(nullptr, 0);
std::string testDir4 = cwd;
free(cwd);
testDir4 += "/";
testDir4 += testDir;
status = celix_utils_createDirectory(testDir4.c_str(), true, &error);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_EQ(error, nullptr);
status = celix_utils_deleteDirectory(testDir4.c_str(), nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
//Can I delete dir containing a dangling symbolic link, which will cause `stat` return ENOENT
status = celix_utils_createDirectory(testDir, true, &error);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_EQ(error, nullptr);
std::string symLink = testDir;
symLink += "/link";
status = symlink("non-existent", symLink.c_str());
EXPECT_EQ(status, CELIX_SUCCESS);
status = celix_utils_deleteDirectory(testDir, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
//Can I delete a dir containing a symbolic link without touch the target
status = celix_utils_createDirectory(testDir, true, &error);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_EQ(error, nullptr);
status = celix_utils_createDirectory(testDir2, true, &error);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_EQ(error, nullptr);
std::string subDir = testDir2;
subDir += "sub";
status = celix_utils_createDirectory(subDir.c_str(), true, &error);
EXPECT_EQ(status, CELIX_SUCCESS);
status = symlink("../directory2", symLink.c_str()); // link -> ../directory2
EXPECT_EQ(status, CELIX_SUCCESS);
status = celix_utils_deleteDirectory(testDir, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_TRUE(celix_utils_fileExists(testDir2));
EXPECT_TRUE(celix_utils_fileExists(subDir.c_str()));
status = celix_utils_deleteDirectory(testDir2, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
}
TEST_F(FileUtilsTestSuite, DeleteFileAsDirectory) {
const std::string root = "celix_file_utils_test";
celix_utils_deleteDirectory(root.c_str(), nullptr);
const char* error = nullptr;
celix_utils_createDirectory(root.c_str(), true, nullptr);
const std::string filename = root + "/file";
std::fstream file(filename, std::ios::out);
file.close();
auto status = celix_utils_deleteDirectory(filename.c_str(), &error);
EXPECT_EQ(status, CELIX_FILE_IO_EXCEPTION);
EXPECT_NE(error, nullptr);
EXPECT_TRUE(celix_utils_fileExists(filename.c_str()));
celix_utils_deleteDirectory(root.c_str(), nullptr);
}
TEST_F(FileUtilsTestSuite, DeleteSymbolicLinkToDirectory) {
const std::string root = "celix_file_utils_test";
const std::string testDir = root + "/directory";
const std::string symLink = root + "/link";
celix_utils_deleteDirectory(root.c_str(), nullptr);
const char* error = nullptr;
celix_utils_createDirectory(testDir.c_str(), true, nullptr);
auto status = symlink("./directory", symLink.c_str()); // link -> ./directory
EXPECT_EQ(status, 0);
status = celix_utils_deleteDirectory(symLink.c_str(), &error);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_EQ(error, nullptr);
EXPECT_FALSE(celix_utils_fileExists(symLink.c_str()));
EXPECT_TRUE(celix_utils_directoryExists(testDir.c_str()));
celix_utils_deleteDirectory(root.c_str(), nullptr);
}
TEST_F(FileUtilsTestSuite, ExtractZipFileTest) {
std::cout << "Using test zip location " << TEST_ZIP_LOCATION << std::endl;
const char* extractLocation = "extract_location";
const char* file1 = "extract_location/top.properties";
const char* file2 = "extract_location/subdir/sub.properties";
celix_utils_deleteDirectory(extractLocation, nullptr);
//Given a test zip file, I can extract this to a provided location and the correct files are extracted
EXPECT_FALSE(celix_utils_fileExists(extractLocation));
auto status = celix_utils_extractZipFile(TEST_ZIP_LOCATION, extractLocation, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_TRUE(celix_utils_fileExists(file1));
celix_properties_t* props = nullptr;
EXPECT_EQ(CELIX_SUCCESS, celix_properties_load(file1, 0, &props));
EXPECT_EQ(celix_properties_getAsLong(props, "level", 0), 1);
celix_properties_destroy(props);
EXPECT_TRUE(celix_utils_fileExists(file2));
props = nullptr;
EXPECT_EQ(CELIX_SUCCESS, celix_properties_load(file2, 0, &props));
EXPECT_NE(props, nullptr);
EXPECT_EQ(celix_properties_getAsLong(props, "level", 0), 2);
celix_properties_destroy(props);
//Given a test zip file, extract to file is a error
const char* error = nullptr;
status = celix_utils_extractZipFile(TEST_ZIP_LOCATION, file1, &error);
EXPECT_NE(status, CELIX_SUCCESS);
EXPECT_NE(error, nullptr);
//Given a incorrect path extractZipFile returns a error
const char* invalidPath = "does-not-exists.zip";
error = nullptr;
EXPECT_FALSE(celix_utils_fileExists(invalidPath));
status = celix_utils_extractZipFile(invalidPath, extractLocation, &error);
EXPECT_NE(status, CELIX_SUCCESS);
EXPECT_NE(error, nullptr);
//Given an non zip file to extractZipFile fails
const char* invalidZip = TEST_A_DIR_LOCATION;
EXPECT_TRUE(celix_utils_fileExists(invalidZip));
status = celix_utils_extractZipFile(invalidZip, extractLocation, &error);
EXPECT_NE(status, CELIX_SUCCESS);
EXPECT_NE(error, nullptr);
}
#ifdef __APPLE__
#include <mach-o/getsect.h>
#include <mach-o/ldsyms.h>
TEST_F(FileUtilsTestSuite, ExtractZipDataTest) {
//Given a zip files linked against the execute-able, can this be handled as a zip data entry.
unsigned long dataSize;
char *data = (char*)getsectiondata(&_mh_execute_header, "resources",
"test_zip", &dataSize);
const char* extractLocation = "extract_location";
const char* file1 = "extract_location/top.properties";
const char* file2 = "extract_location/subdir/sub.properties";
celix_utils_deleteDirectory(extractLocation, nullptr);
//Given test zip data, I can extract this to a provided location and the correct files are extracted
EXPECT_FALSE(celix_utils_fileExists(extractLocation));
//adding 200 extra size to verify this does not result into issues.
auto status = celix_utils_extractZipData(data, dataSize + 200, extractLocation, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_TRUE(celix_utils_fileExists(file1));
celix_properties_t* props = nullptr;
status = celix_properties_load(file1, 0, &props);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_NE(props, nullptr);
EXPECT_EQ(celix_properties_getAsLong(props, "level", 0), 1);
celix_properties_destroy(props);
EXPECT_TRUE(celix_utils_fileExists(file2));
status = celix_properties_load(file2, 0, &props);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_NE(props, nullptr);
EXPECT_EQ(celix_properties_getAsLong(props, "level", 0), 2);
celix_properties_destroy(props);
}
#else
//Given a zip files linked against the execute-able, can this be handled as a zip data entry.
extern const uint8_t test_data_start[] asm("binary_test_zip_start");
extern const uint8_t test_data_end[] asm("binary_test_zip_end");
TEST_F(FileUtilsTestSuite, ExtractZipDataTest) {
const char* extractLocation = "extract_location";
const char* file1 = "extract_location/top.properties";
const char* file2 = "extract_location/subdir/sub.properties";
celix_utils_deleteDirectory(extractLocation, nullptr);
//Given test zip data, I can extract this to a provided location and the correct files are extracted
EXPECT_FALSE(celix_utils_fileExists(extractLocation));
auto status = celix_utils_extractZipData(test_data_start, test_data_end - test_data_start, extractLocation, nullptr);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_TRUE(celix_utils_fileExists(file1));
celix_properties_t* props = nullptr;
EXPECT_EQ(CELIX_SUCCESS, celix_properties_load(file1, 0, &props));
EXPECT_NE(props, nullptr);
EXPECT_EQ(celix_properties_getAsLong(props, "level", 0), 1);
celix_properties_destroy(props);
EXPECT_TRUE(celix_utils_fileExists(file2));
props = nullptr;
EXPECT_EQ(CELIX_SUCCESS, celix_properties_load(file2, 0, &props));
EXPECT_NE(props, nullptr);
EXPECT_EQ(celix_properties_getAsLong(props, "level", 0), 2);
celix_properties_destroy(props);
}
TEST_F(FileUtilsTestSuite, ExtractBadZipDataTest) {
const char* extractLocation = "extract_location";
const char* file1 = "extract_location/top.properties";
const char* file2 = "extract_location/subdir/sub.properties";
celix_utils_deleteDirectory(extractLocation, nullptr);
EXPECT_FALSE(celix_utils_fileExists(extractLocation));
std::vector<uint8_t> zipData(test_data_start, test_data_start+(test_data_end-test_data_start)/2);
auto status = celix_utils_extractZipData(zipData.data(), zipData.size(), extractLocation, nullptr);
EXPECT_NE(status, CELIX_SUCCESS);
EXPECT_FALSE(celix_utils_fileExists(file1));
EXPECT_FALSE(celix_utils_fileExists(file2));
}
#endif
TEST_F(FileUtilsTestSuite, ExtractNullZipDataTest) {
const char* extractLocation = "extract_location";
celix_utils_deleteDirectory(extractLocation, nullptr);
EXPECT_FALSE(celix_utils_fileExists(extractLocation));
const char* error = nullptr;
auto status = celix_utils_extractZipData(nullptr, 1, extractLocation, &error);
EXPECT_EQ(status, CELIX_ERROR_MAKE(CELIX_FACILITY_ZIP,ZIP_ER_INVAL));
EXPECT_NE(error, nullptr);
}
TEST_F(FileUtilsTestSuite, LastModifiedTest) {
//create test dir and test file
const char* testDir = "file_utils_test_dir";
const char* testFile = "file_utils_test_dir/test_file";
celix_utils_deleteDirectory(testDir, nullptr);
EXPECT_FALSE(celix_utils_fileExists(testDir));
EXPECT_FALSE(celix_utils_fileExists(testFile));
celix_utils_createDirectory(testDir, false, nullptr);
FILE* fp = fopen(testFile, "w");
EXPECT_NE(fp, nullptr);
fclose(fp);
EXPECT_TRUE(celix_utils_fileExists(testFile));
//Given a file, I can get the last modified time
struct timespec lastModified{};
auto status = celix_utils_getLastModified(testFile, &lastModified);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_NE(lastModified.tv_sec, 0);
//Given a directory, I can get the last modified time
status = celix_utils_getLastModified(testDir, &lastModified);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_NE(lastModified.tv_sec, 0);
//Given a non-existing file, I can get the last modified time
status = celix_utils_getLastModified("does-not-exists", &lastModified);
EXPECT_NE(status, CELIX_SUCCESS);
EXPECT_EQ(lastModified.tv_sec, 0);
EXPECT_EQ(lastModified.tv_nsec, 0);
}
TEST_F(FileUtilsTestSuite, TouchTest) {
//create test dir and test file
const char* testDir = "file_utils_test_dir";
const char* testFile = "file_utils_test_dir/test_file";
celix_utils_deleteDirectory(testDir, nullptr);
EXPECT_FALSE(celix_utils_fileExists(testDir));
EXPECT_FALSE(celix_utils_fileExists(testFile));
celix_utils_createDirectory(testDir, false, nullptr);
FILE* fp = fopen(testFile, "w");
EXPECT_NE(fp, nullptr);
fclose(fp);
EXPECT_TRUE(celix_utils_fileExists(testFile));
//Given a file, I can touch the file
struct timespec lastModified{};
auto status = celix_utils_getLastModified(testFile, &lastModified);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_NE(lastModified.tv_sec, 0);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
status = celix_utils_touch(testFile);
EXPECT_EQ(status, CELIX_SUCCESS);
struct timespec lastModified2{};
status = celix_utils_getLastModified(testFile, &lastModified2);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_NE(lastModified2.tv_sec, 0);
double diff = celix_difftime(&lastModified, &lastModified2);
EXPECT_LT(diff, 1.0); //should be less than 1 seconds
EXPECT_GT(diff, 0.0); //should be more than 0 seconds
//Given a directory, I can touch the directory
status = celix_utils_getLastModified(testDir, &lastModified);
EXPECT_EQ(status, CELIX_SUCCESS);
EXPECT_NE(lastModified.tv_sec, 0);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
status = celix_utils_touch(testDir);
EXPECT_EQ(status, CELIX_SUCCESS);
status = celix_utils_getLastModified(testDir, &lastModified2);
EXPECT_EQ(status, CELIX_SUCCESS);
diff = celix_difftime(&lastModified, &lastModified2);
EXPECT_LT(diff, 1.0); //should be less than 1 seconds
EXPECT_GT(diff, 0.0); //should be more than 0 seconds
//Given a non-existing file, I cannot touch the file
status = celix_utils_touch("does-not-exists");
EXPECT_NE(status, CELIX_SUCCESS);
}