blob: 615f069e814d1cde2f0e4629a841a72f186bd5b5 [file]
/*
* 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 a
*
* 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 "file/write_file.h"
#include <fcntl.h>
#include <gtest/gtest.h>
#include <fstream>
using namespace storage;
using namespace common;
class WriteFileTest : public ::testing::Test {};
TEST_F(WriteFileTest, CreateFile) {
WriteFile write_file;
std::string file_name = "write_file_test.dat";
remove(file_name.c_str());
int flags = O_WRONLY | O_CREAT | O_TRUNC;
mode_t mode = 0666;
EXPECT_EQ(write_file.create(file_name, flags, mode), E_OK);
EXPECT_TRUE(write_file.file_opened());
EXPECT_EQ(write_file.get_file_path(), file_name);
write_file.close();
remove(file_name.c_str());
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC push_options
#pragma GCC optimize("O0")
#endif
TEST_F(WriteFileTest, WriteToFile) {
WriteFile write_file;
std::string file_name = "test_file_write.dat";
remove(file_name.c_str());
int flags = O_WRONLY | O_CREAT | O_TRUNC;
mode_t mode = 0666;
EXPECT_EQ(write_file.create(file_name, flags, mode), E_OK);
EXPECT_TRUE(write_file.file_opened());
const char* content = "Hello, World!";
uint32_t content_len = strlen(content);
EXPECT_EQ(write_file.write(content, content_len), E_OK);
write_file.close();
std::ifstream file(file_name);
std::string file_content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
EXPECT_EQ(file_content, content);
remove(file_name.c_str());
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC pop_options
#endif
TEST_F(WriteFileTest, SyncFile) {
WriteFile write_file;
std::string file_name = "test_file_sync.dat";
remove(file_name.c_str());
int flags = O_WRONLY | O_CREAT | O_TRUNC;
mode_t mode = 0666;
EXPECT_EQ(write_file.create(file_name, flags, mode), E_OK);
EXPECT_TRUE(write_file.file_opened());
const char* content = "Hello, Sync!";
uint32_t content_len = strlen(content);
EXPECT_EQ(write_file.write(content, content_len), E_OK);
EXPECT_EQ(write_file.sync(), E_OK);
write_file.close();
remove(file_name.c_str());
}
TEST_F(WriteFileTest, CloseFile) {
WriteFile write_file;
std::string file_name = "test_file_close.dat";
remove(file_name.c_str());
int flags = O_WRONLY | O_CREAT | O_TRUNC;
mode_t mode = 0666;
EXPECT_EQ(write_file.create(file_name, flags, mode), E_OK);
EXPECT_TRUE(write_file.file_opened());
const char* content = "Closing file.";
uint32_t content_len = strlen(content);
EXPECT_EQ(write_file.write(content, content_len), E_OK);
EXPECT_EQ(write_file.close(), E_OK);
}
// Truncate file to a given size (used by RestorableTsFileIOWriter after
// recovery).
TEST_F(WriteFileTest, TruncateFile) {
WriteFile write_file;
std::string file_name = "test_file_truncate.dat";
remove(file_name.c_str());
int flags = O_RDWR | O_CREAT | O_TRUNC;
#ifdef _WIN32
flags |= O_BINARY;
#endif
mode_t mode = 0666;
EXPECT_EQ(write_file.create(file_name, flags, mode), E_OK);
EXPECT_TRUE(write_file.file_opened());
const char* content = "Hello, Truncate World!";
uint32_t content_len = strlen(content);
EXPECT_EQ(write_file.write(content, content_len), E_OK);
EXPECT_EQ(write_file.truncate(7), E_OK);
write_file.close();
std::ifstream file(file_name);
std::string file_content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
EXPECT_EQ(file_content, "Hello, ");
remove(file_name.c_str());
}
#include "file/tsfile_io_writer.h"
// Regression: TsFileIOWriter::init() used to leave destroyed_=true after a
// previous destroy(), so the second destroy() (during ~TsFileIOWriter())
// short-circuited and skipped meta_allocator_.destroy() /
// write_stream_.destroy() / file_ cleanup, leaking everything from the
// new lifecycle. Verify init() rearms the lifecycle by checking destroy()
// runs again cleanly.
TEST(TsFileIOWriterLifecycle, DestroyInitDestroyIsClean) {
std::string fn = "tsfile_iowriter_lifecycle.dat";
remove(fn.c_str());
WriteFile wf1;
int flags = O_WRONLY | O_CREAT | O_TRUNC;
#ifdef _WIN32
flags |= O_BINARY;
#endif
ASSERT_EQ(wf1.create(fn, flags, 0666), E_OK);
TsFileIOWriter w;
ASSERT_EQ(w.init(&wf1), E_OK);
w.destroy();
// Re-init against a fresh WriteFile (same writer object). Under the
// old bug, destroyed_ stays true here.
remove(fn.c_str());
WriteFile wf2;
ASSERT_EQ(wf2.create(fn, flags, 0666), E_OK);
ASSERT_EQ(w.init(&wf2), E_OK);
// get_meta_size() reads meta_allocator_.get_total_used_bytes(); on a
// fresh init() this should be 0 (the allocator was reinitialised).
// If destroyed_ had been left true the allocator pages from before
// would still be there.
EXPECT_EQ(w.get_meta_size(), 0);
// Trigger second destroy() — must not crash on the re-initialised
// resources.
w.destroy();
wf2.close();
remove(fn.c_str());
}