14fd969f2STrond Norbye/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
24fd969f2STrond Norbye/*
34fd969f2STrond Norbye *     Copyright 2016 Couchbase, Inc.
44fd969f2STrond Norbye *
54fd969f2STrond Norbye *   Licensed under the Apache License, Version 2.0 (the "License");
64fd969f2STrond Norbye *   you may not use this file except in compliance with the License.
74fd969f2STrond Norbye *   You may obtain a copy of the License at
84fd969f2STrond Norbye *
94fd969f2STrond Norbye *       http://www.apache.org/licenses/LICENSE-2.0
104fd969f2STrond Norbye *
114fd969f2STrond Norbye *   Unless required by applicable law or agreed to in writing, software
124fd969f2STrond Norbye *   distributed under the License is distributed on an "AS IS" BASIS,
134fd969f2STrond Norbye *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
144fd969f2STrond Norbye *   See the License for the specific language governing permissions and
154fd969f2STrond Norbye *   limitations under the License.
164fd969f2STrond Norbye */
174fd969f2STrond Norbye#include <gtest/gtest.h>
184fd969f2STrond Norbye
194fd969f2STrond Norbye#include <algorithm>
204fd969f2STrond Norbye#include <cerrno>
2169030100STrond Norbye#include <cstdlib>
224fd969f2STrond Norbye#include <cstring>
23d3828d9fSTrond Norbye#include <limits>
244fd969f2STrond Norbye#include <platform/dirutils.h>
254fd969f2STrond Norbye#include <stdio.h>
2669030100STrond Norbye#include <string>
2769030100STrond Norbye#include <sys/stat.h>
2869030100STrond Norbye#include <sys/types.h>
294fd969f2STrond Norbye#include <vector>
30e688f59fSSriram Ganesan#include <system_error>
3169030100STrond Norbye
3269030100STrond Norbye#ifdef WIN32
3369030100STrond Norbye#include <windows.h>
3469030100STrond Norbye#include <direct.h>
354fd969f2STrond Norbye#define mkdir(a, b) _mkdir(a)
364fd969f2STrond Norbye#define PATH_SEPARATOR "\\"
374fd969f2STrond Norbye#else
384fd969f2STrond Norbye#define PATH_SEPARATOR "/"
394fd969f2STrond Norbye#endif
404fd969f2STrond Norbye
414fd969f2STrond Norbyestatic bool CreateDirectory(const std::string& dir) {
424fd969f2STrond Norbye    if (mkdir(dir.c_str(), S_IXUSR | S_IWUSR | S_IRUSR) != 0) {
434fd969f2STrond Norbye        if (errno != EEXIST) {
444fd969f2STrond Norbye            return false;
454fd969f2STrond Norbye        }
4669030100STrond Norbye    }
4769030100STrond Norbye    return true;
4869030100STrond Norbye}
4969030100STrond Norbye
504fd969f2STrond Norbyestatic bool exists(const std::string& path) {
514fd969f2STrond Norbye    struct stat st;
524fd969f2STrond Norbye    return stat(path.c_str(), &st) == 0;
5369030100STrond Norbye}
5469030100STrond Norbye
5569030100STrond Norbye
564fd969f2STrond Norbyeclass IoTest : public ::testing::Test {
574fd969f2STrond Norbyepublic:
5869030100STrond Norbye
594fd969f2STrond Norbye    static void SetUpTestCase();
6069030100STrond Norbye
614fd969f2STrond Norbye    static void TearDownTestCase();
6269030100STrond Norbye
634fd969f2STrond Norbyeprotected:
64877ba0c5STrond Norbye
654fd969f2STrond Norbye    // A small filesystem we can play around with
664fd969f2STrond Norbye    static const std::vector<std::string> vfs;
674fd969f2STrond Norbye};
6869030100STrond Norbye
694fd969f2STrond Norbyeconst std::vector<std::string> IoTest::vfs = {
704fd969f2STrond Norbye    {"fs"},
714fd969f2STrond Norbye    {"fs/d1"},
724fd969f2STrond Norbye    {"fs/d2"},
734fd969f2STrond Norbye    {"fs/e2"},
744fd969f2STrond Norbye    {"fs/f2c"},
754fd969f2STrond Norbye    {"fs/g2"},
764fd969f2STrond Norbye    {"fs/d3"},
774fd969f2STrond Norbye    {"fs/1"},
784fd969f2STrond Norbye    {"fs/2"},
794fd969f2STrond Norbye    {"fs/2c"},
804fd969f2STrond Norbye    {"fs/2d"},
81ea4f023dSJim Walker    {"fs/3"},
82ea4f023dSJim Walker    {"fs/d1/d1"}
834fd969f2STrond Norbye};
8469030100STrond Norbye
854fd969f2STrond Norbyevoid IoTest::SetUpTestCase() {
864fd969f2STrond Norbye    for (const auto& file : vfs) {
874fd969f2STrond Norbye        ASSERT_TRUE(CreateDirectory(file)) << "failed to create directory";
884fd969f2STrond Norbye        ASSERT_TRUE(exists(file)) << "directory " << file << " does not exists";
894fd969f2STrond Norbye    }
9069030100STrond Norbye}
9169030100STrond Norbye
924fd969f2STrond Norbyevoid IoTest::TearDownTestCase() {
934fd969f2STrond Norbye    // Expecting the rmrf to work ;)
944fd969f2STrond Norbye    cb::io::rmrf("fs");
9569030100STrond Norbye}
9669030100STrond Norbye
974fd969f2STrond NorbyeTEST_F(IoTest, dirname) {
984fd969f2STrond Norbye    // Check the simple case
994fd969f2STrond Norbye    EXPECT_EQ("foo", cb::io::dirname("foo\\bar"));
1004fd969f2STrond Norbye    EXPECT_EQ("foo", cb::io::dirname("foo/bar"));
1014fd969f2STrond Norbye
1024fd969f2STrond Norbye    // Make sure that we remove double an empty chunk
1034fd969f2STrond Norbye    EXPECT_EQ("foo", cb::io::dirname("foo\\\\bar"));
1044fd969f2STrond Norbye    EXPECT_EQ("foo", cb::io::dirname("foo//bar"));
1054fd969f2STrond Norbye
1064fd969f2STrond Norbye    // Make sure that we handle the case without a directory
1074fd969f2STrond Norbye    EXPECT_EQ(".", cb::io::dirname("bar"));
1084fd969f2STrond Norbye    EXPECT_EQ(".", cb::io::dirname(""));
1094fd969f2STrond Norbye
1104fd969f2STrond Norbye    // Absolute directories
1114fd969f2STrond Norbye    EXPECT_EQ("\\", cb::io::dirname("\\bar"));
1124fd969f2STrond Norbye    EXPECT_EQ("\\", cb::io::dirname("\\\\bar"));
1134fd969f2STrond Norbye    EXPECT_EQ("/", cb::io::dirname("/bar"));
1144fd969f2STrond Norbye    EXPECT_EQ("/", cb::io::dirname("//bar"));
1154fd969f2STrond Norbye
1164fd969f2STrond Norbye    // Test that we work with multiple directories
1174fd969f2STrond Norbye    EXPECT_EQ("1/2/3/4/5", cb::io::dirname("1/2/3/4/5/6"));
1184fd969f2STrond Norbye    EXPECT_EQ("1\\2\\3\\4\\5", cb::io::dirname("1\\2\\3\\4\\5\\6"));
1194fd969f2STrond Norbye    EXPECT_EQ("1/2\\4/5", cb::io::dirname("1/2\\4/5\\6"));
12069030100STrond Norbye}
12169030100STrond Norbye
1224fd969f2STrond NorbyeTEST_F(IoTest, basename) {
1234fd969f2STrond Norbye    EXPECT_EQ("bar", cb::io::basename("foo\\bar"));
1244fd969f2STrond Norbye    EXPECT_EQ("bar", cb::io::basename("foo/bar"));
1254fd969f2STrond Norbye    EXPECT_EQ("bar", cb::io::basename("foo\\\\bar"));
1264fd969f2STrond Norbye    EXPECT_EQ("bar", cb::io::basename("foo//bar"));
1274fd969f2STrond Norbye    EXPECT_EQ("bar", cb::io::basename("bar"));
1284fd969f2STrond Norbye    EXPECT_EQ("", cb::io::basename(""));
1294fd969f2STrond Norbye    EXPECT_EQ("bar", cb::io::basename("\\bar"));
1304fd969f2STrond Norbye    EXPECT_EQ("bar", cb::io::basename("\\\\bar"));
1314fd969f2STrond Norbye    EXPECT_EQ("bar", cb::io::basename("/bar"));
1324fd969f2STrond Norbye    EXPECT_EQ("bar", cb::io::basename("//bar"));
1334fd969f2STrond Norbye    EXPECT_EQ("6", cb::io::basename("1/2/3/4/5/6"));
1344fd969f2STrond Norbye    EXPECT_EQ("6", cb::io::basename("1\\2\\3\\4\\5\\6"));
1354fd969f2STrond Norbye    EXPECT_EQ("6", cb::io::basename("1/2\\4/5\\6"));
13669030100STrond Norbye}
13769030100STrond Norbye
1384fd969f2STrond NorbyeTEST_F(IoTest, findFilesWithPrefix) {
1394fd969f2STrond Norbye    auto vec = cb::io::findFilesWithPrefix("fs");
1401ebd7828SDave Rigby    EXPECT_EQ(1u, vec.size());
14169030100STrond Norbye
1424fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1434fd969f2STrond Norbye                                   "." PATH_SEPARATOR "fs"));
14469030100STrond Norbye
14569030100STrond Norbye
1464fd969f2STrond Norbye    vec = cb::io::findFilesWithPrefix("fs", "d");
1471ebd7828SDave Rigby    EXPECT_EQ(3u, vec.size());
14869030100STrond Norbye
1494fd969f2STrond Norbye    // We don't know the order of the files in the result..
1504fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1514fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "d1"));
1524fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1534fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "d2"));
1544fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1554fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "d3"));
15669030100STrond Norbye
1574fd969f2STrond Norbye    vec = cb::io::findFilesWithPrefix("fs", "1");
1581ebd7828SDave Rigby    EXPECT_EQ(1u, vec.size());
1594fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1604fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "1"));
16169030100STrond Norbye
1624fd969f2STrond Norbye    vec = cb::io::findFilesWithPrefix("fs", "");
163ea4f023dSJim Walker    EXPECT_EQ(vfs.size() - 2, vec.size());
16469030100STrond Norbye}
16569030100STrond Norbye
1664fd969f2STrond NorbyeTEST_F(IoTest, findFilesContaining) {
1674fd969f2STrond Norbye    auto vec = cb::io::findFilesContaining("fs", "");
168ea4f023dSJim Walker    EXPECT_EQ(vfs.size() - 2, vec.size());
1694fd969f2STrond Norbye
1704fd969f2STrond Norbye    vec = cb::io::findFilesContaining("fs", "2");
1711ebd7828SDave Rigby    EXPECT_EQ(7u, vec.size());
1724fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1734fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "d2"));
1744fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1754fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "e2"));
1764fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1774fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "f2c"));
1784fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1794fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "g2"));
1804fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1814fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "2"));
1824fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1834fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "2c"));
1844fd969f2STrond Norbye    EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(),
1854fd969f2STrond Norbye                                   "fs" PATH_SEPARATOR "2d"));
186877ba0c5STrond Norbye}
187877ba0c5STrond Norbye
1884fd969f2STrond NorbyeTEST_F(IoTest, mktemp) {
1894fd969f2STrond Norbye    auto filename = cb::io::mktemp("foo");
1904fd969f2STrond Norbye    EXPECT_FALSE(filename.empty())
1914fd969f2STrond Norbye                << "FAIL: Expected to create tempfile without mask";
1924fd969f2STrond Norbye    EXPECT_TRUE(cb::io::isFile(filename));
193e688f59fSSriram Ganesan    EXPECT_NO_THROW(cb::io::rmrf(filename));
1944fd969f2STrond Norbye    EXPECT_FALSE(cb::io::isFile(filename));
1954fd969f2STrond Norbye    EXPECT_FALSE(cb::io::isDirectory(filename));
1964fd969f2STrond Norbye
1974fd969f2STrond Norbye    filename = cb::io::mktemp("barXXXXXX");
1984fd969f2STrond Norbye    EXPECT_FALSE(filename.empty())
1994fd969f2STrond Norbye                << "FAIL: Expected to create tempfile mask";
2004fd969f2STrond Norbye    EXPECT_TRUE(cb::io::isFile(filename));
201e688f59fSSriram Ganesan    EXPECT_NO_THROW(cb::io::rmrf(filename));
2024fd969f2STrond Norbye    EXPECT_FALSE(cb::io::isFile(filename));
2034fd969f2STrond Norbye    EXPECT_FALSE(cb::io::isDirectory(filename));
204c681fee5STrond Norbye}
205c681fee5STrond Norbye
2064fd969f2STrond NorbyeTEST_F(IoTest, isFileAndIsDirectory) {
2074fd969f2STrond Norbye    EXPECT_FALSE(cb::io::isFile("."));
2084fd969f2STrond Norbye    EXPECT_TRUE(cb::io::isDirectory("."));
2094fd969f2STrond Norbye    auto filename = cb::io::mktemp("plainfile");
2104fd969f2STrond Norbye    EXPECT_TRUE(cb::io::isFile(filename));
2114fd969f2STrond Norbye    EXPECT_FALSE(cb::io::isDirectory(filename));
212e688f59fSSriram Ganesan    EXPECT_NO_THROW(cb::io::rmrf(filename));
213e688f59fSSriram Ganesan}
214e688f59fSSriram Ganesan
215e688f59fSSriram GanesanTEST_F(IoTest, removeNonExistentFile) {
216e688f59fSSriram Ganesan    EXPECT_THROW(cb::io::rmrf("foo"), std::system_error)
217e688f59fSSriram Ganesan                << "Expected system error for removing non-existent file";
21846f2da31STrond Norbye}
21946f2da31STrond Norbye
2204fd969f2STrond NorbyeTEST_F(IoTest, getcwd) {
2214fd969f2STrond Norbye    auto cwd = cb::io::getcwd();
2224fd969f2STrond Norbye    // I can't really determine the correct value here, but it shouldn't be
2234fd969f2STrond Norbye    // empty ;-)
2244fd969f2STrond Norbye    ASSERT_FALSE(cwd.empty());
22546f2da31STrond Norbye}
22646f2da31STrond Norbye
2274fd969f2STrond NorbyeTEST_F(IoTest, mkdirp) {
228de77d527STrond Norbye    std::string path{"/it/would/suck/if/I/could/create/this"};
229de77d527STrond Norbye
230de77d527STrond Norbye#ifdef WIN32
231de77d527STrond Norbye    // Try to use an invalid drive on Windows
232de77d527STrond Norbye    bool found = false;
233de77d527STrond Norbye    for (int ii = 'D'; ii < 'Z'; ++ii) {
234de77d527STrond Norbye        std::string drive;
235de77d527STrond Norbye        drive.push_back((char)ii);
236de77d527STrond Norbye        drive.push_back(':');
237de77d527STrond Norbye        if (!cb::io::isDirectory(drive)) {
238de77d527STrond Norbye            found = true;
239de77d527STrond Norbye            path = drive + path;
240de77d527STrond Norbye            break;
241de77d527STrond Norbye        }
242de77d527STrond Norbye    }
243de77d527STrond Norbye    if (!found) {
244de77d527STrond Norbye        FAIL() << "Failed to locate an unused drive";
245de77d527STrond Norbye    }
2464fd969f2STrond Norbye#endif
247de77d527STrond Norbye    EXPECT_THROW(cb::io::mkdirp(path), std::runtime_error);
248de77d527STrond Norbye
249e6a1e8c9STrond Norbye    EXPECT_NO_THROW(cb::io::mkdirp("."));
250e6a1e8c9STrond Norbye    EXPECT_NO_THROW(cb::io::mkdirp("/"));
251e6a1e8c9STrond Norbye    EXPECT_NO_THROW(cb::io::mkdirp("foo/bar"));
252e6a1e8c9STrond Norbye    EXPECT_NO_THROW(cb::io::isDirectory("foo/bar"));
2534fd969f2STrond Norbye    cb::io::rmrf("foo");
2544fd969f2STrond Norbye    EXPECT_FALSE(cb::io::isDirectory("foo/bar"));
2554fd969f2STrond Norbye    EXPECT_FALSE(cb::io::isDirectory("foo"));
256d3828d9fSTrond Norbye}
257d3828d9fSTrond Norbye
2584fd969f2STrond NorbyeTEST_F(IoTest, maximizeFileDescriptors) {
2594fd969f2STrond Norbye    auto limit = cb::io::maximizeFileDescriptors(32);
2601ebd7828SDave Rigby    EXPECT_LE(32u, limit) << "FAIL: I should be able to set it to at least 32";
2614fd969f2STrond Norbye
2624fd969f2STrond Norbye    limit = cb::io::maximizeFileDescriptors(
2634fd969f2STrond Norbye        std::numeric_limits<uint32_t>::max());
2644fd969f2STrond Norbye    if (limit != std::numeric_limits<uint32_t>::max()) {
2654fd969f2STrond Norbye        // windows don't have a max limit, and that could be for other platforms
2664fd969f2STrond Norbye        // as well..
2674fd969f2STrond Norbye        EXPECT_EQ(limit, cb::io::maximizeFileDescriptors(limit + 1))
2684fd969f2STrond Norbye             << "FAIL: I expected maximizeFileDescriptors to return "
2694fd969f2STrond Norbye                      << "the same max limit two times in a row";
2704fd969f2STrond Norbye    }
271d3828d9fSTrond Norbye
2724fd969f2STrond Norbye    limit = cb::io::maximizeFileDescriptors(
2734fd969f2STrond Norbye        std::numeric_limits<uint64_t>::max());
2744fd969f2STrond Norbye    if (limit != std::numeric_limits<uint64_t>::max()) {
2754fd969f2STrond Norbye        // windows don't have a max limit, and that could be for other platforms
2764fd969f2STrond Norbye        // as well..
2774fd969f2STrond Norbye        EXPECT_EQ(limit, cb::io::maximizeFileDescriptors(limit + 1))
2784fd969f2STrond Norbye                    << "FAIL: I expected maximizeFileDescriptors to return "
2794fd969f2STrond Norbye                    << "the same max limit two times in a row";
2804fd969f2STrond Norbye    }
28169030100STrond Norbye}
282