/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2019 Telegram Systems LLP */ #include "td/utils/common.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/port/FileFd.h" #include "td/utils/port/IoSlice.h" #include "td/utils/port/path.h" #include "td/utils/port/signals.h" #include "td/utils/port/thread.h" #include "td/utils/port/thread_local.h" #include "td/utils/Slice.h" #include "td/utils/tests.h" using namespace td; TEST(Port, files) { CSlice main_dir = "test_dir"; rmrf(main_dir).ignore(); ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error()); ASSERT_TRUE(walk_path(main_dir, [](CSlice name, WalkPath::Type type) { UNREACHABLE(); }).is_error()); mkdir(main_dir).ensure(); mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "A").ensure(); mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B").ensure(); mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B" << TD_DIR_SLASH << "D").ensure(); mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "C").ensure(); ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error()); std::string fd_path = PSTRING() << main_dir << TD_DIR_SLASH << "t.txt"; std::string fd2_path = PSTRING() << main_dir << TD_DIR_SLASH << "C" << TD_DIR_SLASH << "t2.txt"; auto fd = FileFd::open(fd_path, FileFd::Write | FileFd::CreateNew).move_as_ok(); auto fd2 = FileFd::open(fd2_path, FileFd::Write | FileFd::CreateNew).move_as_ok(); fd2.close(); int cnt = 0; const int ITER_COUNT = 1000; for (int i = 0; i < ITER_COUNT; i++) { walk_path(main_dir, [&](CSlice name, WalkPath::Type type) { if (type == WalkPath::Type::NotDir) { ASSERT_TRUE(name == fd_path || name == fd2_path); } cnt++; }) .ensure(); } ASSERT_EQ((5 * 2 + 2) * ITER_COUNT, cnt); bool was_abort = false; walk_path(main_dir, [&](CSlice name, WalkPath::Type type) { CHECK(!was_abort); if (type == WalkPath::Type::EnterDir && ends_with(name, PSLICE() << TD_DIR_SLASH << "B")) { was_abort = true; return WalkPath::Action::Abort; } return WalkPath::Action::Continue; }) .ensure(); CHECK(was_abort); cnt = 0; bool is_first_dir = true; walk_path(main_dir, [&](CSlice name, WalkPath::Type type) { cnt++; if (type == WalkPath::Type::EnterDir) { if (is_first_dir) { is_first_dir = false; } else { return WalkPath::Action::SkipDir; } } return WalkPath::Action::Continue; }) .ensure(); ASSERT_EQ(6, cnt); ASSERT_EQ(0u, fd.get_size().move_as_ok()); ASSERT_EQ(12u, fd.write("Hello world!").move_as_ok()); ASSERT_EQ(4u, fd.pwrite("abcd", 1).move_as_ok()); char buf[100]; MutableSlice buf_slice(buf, sizeof(buf)); ASSERT_TRUE(fd.pread(buf_slice.substr(0, 4), 2).is_error()); fd.seek(11).ensure(); ASSERT_EQ(2u, fd.write("?!").move_as_ok()); ASSERT_TRUE(FileFd::open(main_dir, FileFd::Read | FileFd::CreateNew).is_error()); fd = FileFd::open(fd_path, FileFd::Read | FileFd::Create).move_as_ok(); ASSERT_EQ(13u, fd.get_size().move_as_ok()); ASSERT_EQ(4u, fd.pread(buf_slice.substr(0, 4), 1).move_as_ok()); ASSERT_STREQ("abcd", buf_slice.substr(0, 4)); fd.seek(0).ensure(); ASSERT_EQ(13u, fd.read(buf_slice.substr(0, 13)).move_as_ok()); ASSERT_STREQ("Habcd world?!", buf_slice.substr(0, 13)); } TEST(Port, Writev) { std::vector vec; CSlice test_file_path = "test.txt"; unlink(test_file_path).ignore(); auto fd = FileFd::open(test_file_path, FileFd::Write | FileFd::CreateNew).move_as_ok(); vec.push_back(as_io_slice("a")); vec.push_back(as_io_slice("b")); vec.push_back(as_io_slice("cd")); ASSERT_EQ(4u, fd.writev(vec).move_as_ok()); vec.clear(); vec.push_back(as_io_slice("efg")); vec.push_back(as_io_slice("")); vec.push_back(as_io_slice("hi")); ASSERT_EQ(5u, fd.writev(vec).move_as_ok()); fd.close(); fd = FileFd::open(test_file_path, FileFd::Read).move_as_ok(); Slice expected_content = "abcdefghi"; ASSERT_EQ(static_cast(expected_content.size()), fd.get_size().ok()); std::string content(expected_content.size(), '\0'); ASSERT_EQ(content.size(), fd.read(content).move_as_ok()); ASSERT_EQ(expected_content, content); } #if TD_PORT_POSIX && !TD_THREAD_UNSUPPORTED #include #include #include #include #include static std::mutex m; static std::vector ptrs; static std::vector addrs; static TD_THREAD_LOCAL int thread_id; static void on_user_signal(int sig) { int addr; addrs[thread_id] = &addr; char ptr[10]; snprintf(ptr, 6, "%d", thread_id); std::unique_lock guard(m); ptrs.push_back(std::string(ptr)); } TEST(Post, SignalsAndThread) { setup_signals_alt_stack().ensure(); set_signal_handler(SignalType::User, on_user_signal).ensure(); std::vector ans = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; { std::vector threads; int thread_n = 10; std::vector stages(thread_n); ptrs.clear(); addrs.resize(thread_n); for (int i = 0; i < 10; i++) { threads.emplace_back([&, i] { setup_signals_alt_stack().ensure(); if (i != 0) { stages[i].wait(2); } thread_id = i; pthread_kill(pthread_self(), SIGUSR1); if (i + 1 < thread_n) { stages[i + 1].wait(2); } }); } for (auto &t : threads) { t.join(); } CHECK(ptrs == ans); LOG(ERROR) << ptrs; //LOG(ERROR) << std::set(addrs.begin(), addrs.end()).size(); //LOG(ERROR) << addrs; } { Stage stage; std::vector threads; int thread_n = 10; ptrs.clear(); addrs.resize(thread_n); for (int i = 0; i < 10; i++) { threads.emplace_back([&, i] { stage.wait(thread_n); thread_id = i; pthread_kill(pthread_self(), SIGUSR1); //kill(pid_t(syscall(SYS_gettid)), SIGUSR1); }); } for (auto &t : threads) { t.join(); } std::sort(ptrs.begin(), ptrs.end()); CHECK(ptrs == ans); std::sort(addrs.begin(), addrs.end()); ASSERT_TRUE(std::unique(addrs.begin(), addrs.end()) == addrs.end()); //LOG(ERROR) << addrs; } } #endif