//------------------------------------------------------------------------------
//! @file XrdFstOfsFileTest.cc
//! @author Elvin Sindrilaru - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2020 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "gtest/gtest.h"
#include "TestEnv.hh"
#include "fst/layout/RainMetaLayout.hh"
#include "fst/checksum/CRC32C.hh"
#include "fst/layout/RaidDpLayout.hh"
#include "fst/io/xrd/XrdIo.hh"
#include "XrdOuc/XrdOucTokenizer.hh"
#include
EOSFSTTEST_NAMESPACE_BEGIN
//------------------------------------------------------------------------------
// Write test - not useful at the moment
//------------------------------------------------------------------------------
TEST(FstFileTest, DISABLED_WriteTest)
{
using namespace XrdCl;
std::unique_ptr mFile(new File());
// Initialize
uint32_t file_size = (uint32_t) atoi(gEnv->GetMapping("file_size").c_str());
std::string address = "root://root@" + gEnv->GetMapping("server");
std::string file_path = gEnv->GetMapping("replica_file");
// Validate URL
URL url(address);
ASSERT_TRUE(url.IsValid());
// Open file
std::string file_url = address + "/" + file_path;
GLOG << "Opening file: " << file_url << std::endl;
XRootDStatus status = mFile->Open(file_url,
OpenFlags::Update | OpenFlags::Delete,
Access::Mode::None);
ASSERT_TRUE(status.IsOK());
// Prepare 1MB chunk
uint32_t size_chunk = 1024 * 1024;
int num_chunks = file_size / size_chunk;
char* buff_write = new char[size_chunk];
std::ifstream urandom("/dev/urandom", std::ios::in | std::ios::binary);
urandom.read(buff_write, size_chunk);
urandom.close();
// Write the file in 1MB chunks
GLOG << "Performing write operation" << std::endl;
for (int i = 0; i < num_chunks; i++) {
uint64_t offset = (uint64_t) i * size_chunk ;
status = mFile->Write(offset, size_chunk, buff_write);
if (!status.IsOK()) {
std::cerr << "Error while writing at offset:" << offset << std::endl;
std::terminate();
}
}
// Close file
status = mFile->Close();
ASSERT_TRUE(status.IsOK());
delete[] buff_write;
}
//------------------------------------------------------------------------------
// Vector read test
//------------------------------------------------------------------------------
TEST(FstFileTest, ReadVTest)
{
using namespace XrdCl;
std::unique_ptr mFile(new File());
uint32_t nread;
// Initialize
uint32_t file_size = (uint32_t) atoi(gEnv->GetMapping("file_size").c_str());
std::string address = "root://root@" + gEnv->GetMapping("server");
std::string file_path = gEnv->GetMapping("raiddp_file");
// Validate URL
URL url(address);
ASSERT_TRUE(url.IsValid());
// Open file
std::string file_url = address + "/" + file_path;
GLOG << "Opening file: " << file_url << std::endl;
XRootDStatus status = mFile->Open(file_url, OpenFlags::Read,
Access::Mode::None);
ASSERT_TRUE(status.IsOK());
// Check file has the proper size
StatInfo* stat = nullptr;
status = mFile->Stat(false, stat);
ASSERT_TRUE(status.IsOK());
GLOG << "Stat size: " << stat->GetSize() << std::endl;
ASSERT_EQ(stat->GetSize(), file_size);
delete stat;
// Read the first 4KB out of each MB
uint32_t size_chunk = 4096;
uint32_t size_gap = 1024 * 1024;
int num_chunks = file_size / size_gap;
char* ptr_readv;
char* ptr_read;
char* buff_readv = new char[num_chunks * size_chunk];
char* buff_read = new char[num_chunks * size_chunk];
ChunkList readv_list;
ChunkList read_list;
// Create the readV list and the list for normal reads
for (int i = 0; i < num_chunks; i++) {
uint64_t off = (uint64_t) i * size_gap ;
ptr_readv = buff_readv + i * size_chunk;
readv_list.push_back(ChunkInfo(off, size_chunk, ptr_readv));
ptr_read = buff_read + i * size_chunk;
read_list.push_back(ChunkInfo(off, size_chunk, ptr_read));
}
// Issue the readV request
GLOG << "Performing readV operation" << std::endl;
VectorReadInfo* vread_info = nullptr;
status = mFile->VectorRead(readv_list, 0, vread_info);
ASSERT_TRUE(status.IsOK());
ASSERT_EQ(num_chunks * size_chunk, vread_info->GetSize());
delete vread_info;
// Issue the normal read requests
GLOG << "Performing normal read operation" << std::endl;
for (auto chunk = read_list.begin(); chunk != read_list.end(); ++chunk) {
status = mFile->Read(chunk->offset, chunk->length, chunk->buffer, nread);
if (!status.IsOK() || (nread != chunk->length)) {
std::cerr << "Error while reading at offset:" << chunk->offset
<< " len:" << chunk->length << std::endl;
std::terminate();
}
}
// Compute CRC32C checksum for the readV buffer
eos::fst::CRC32C* chksumv = new eos::fst::CRC32C();
if (!chksumv->Add(buff_readv, num_chunks * size_chunk, 0)) {
std::cerr << "Checksum error: offset unaligned - skip computation" << std::endl;
std::terminate();
}
// Compute CRC32C checksum for the normal read buffer
eos::fst::CRC32C* chksum = new eos::fst::CRC32C();
if (!chksum->Add(buff_read, num_chunks * size_chunk, 0)) {
std::cerr << "Checksum error: offset unaligned - skip computation" << std::endl;
std::terminate();
}
// Compare checksums
std::string schksumv = chksumv->GetHexChecksum();
std::string schksum = chksum->GetHexChecksum();
GLOG << "ChecksumV: " << schksumv << std::endl;
GLOG << "Checksum: " << schksum << std::endl;
ASSERT_STREQ(schksum.c_str(), schksumv.c_str());
// Close file
status = mFile->Close();
ASSERT_TRUE(status.IsOK());
delete[] buff_readv;
delete[] buff_read;
}
//------------------------------------------------------------------------------
// Split vector read test
//------------------------------------------------------------------------------
TEST(FstFileTest, SplitReadVTest)
{
using namespace eos::common;
using namespace eos::fst;
unsigned long layout_id = LayoutId::GetId(LayoutId::kRaid6, 1,
6,
LayoutId::k1M,
LayoutId::kCRC32);
std::unique_ptr file(new RaidDpLayout(NULL, layout_id, NULL, NULL,
"root://localhost//dummy"));
// Create readV request
int num_datasets = 4;
char* buff = new char[1024 * 1024];
XrdCl::ChunkList readV;
XrdCl::ChunkList correct_rdv;
std::ostringstream sstr;
std::string str_off;
std::string str_len;
char* ptr_off;
char* ptr_len;
// Loop through all the sets of data in the test environment
for (int i = 1; i < num_datasets; i++) {
// Read the initial offsets
sstr.str("");
sstr << "off" << i;
str_off = gEnv->GetMapping(sstr.str());
XrdOucTokenizer tok_off = XrdOucTokenizer((char*) str_off.c_str());
// Read the initial lengths
sstr.str("");
sstr << "len" << i;
str_len = gEnv->GetMapping(sstr.str());
XrdOucTokenizer tok_len = XrdOucTokenizer((char*) str_len.c_str());
ptr_off = tok_off.GetLine();
ptr_len = tok_len.GetLine();
while ((ptr_off = tok_off.GetToken()) && (ptr_len = tok_len.GetToken())) {
readV.push_back(XrdCl::ChunkInfo((uint64_t) atoi(ptr_off),
(uint32_t) atoi(ptr_len),
(void*) 0));
}
int indx = 0;
std::vector result = ((RainMetaLayout*)
file.get())->SplitReadV(readV);
// Loop through the answers for each stripe and compare with the correct values
for (auto it_stripe = result.begin(); it_stripe != result.end(); ++it_stripe) {
//Read the correct answer to compare with
sstr.str("");
sstr << "off" << i << "_stripe" << indx;
str_off = gEnv->GetMapping(sstr.str());
tok_off = XrdOucTokenizer((char*)str_off.c_str());
sstr.str("");
sstr << "len" << i << "_stripe" << indx;
str_len = gEnv->GetMapping(sstr.str());
tok_len = XrdOucTokenizer((char*)str_len.c_str());
ptr_off = tok_off.GetLine();
ptr_len = tok_len.GetLine();
correct_rdv.clear();
while ((ptr_off = tok_off.GetToken()) && (ptr_len = tok_len.GetToken())) {
correct_rdv.push_back(XrdCl::ChunkInfo((uint64_t) atoi(ptr_off),
(uint32_t) atoi(ptr_len),
buff));
}
// Test same length
ASSERT_EQ(correct_rdv.size(), it_stripe->size());
// Test each individual chunk
for (auto it_chunk = it_stripe->begin(), it_resp = correct_rdv.begin();
it_chunk != it_stripe->end(); ++it_chunk, ++it_resp) {
ASSERT_EQ(it_resp->offset, it_chunk->offset);
ASSERT_EQ(it_resp->length, it_chunk->length);
}
indx++;
}
readV.clear();
}
// Free memory
delete[] buff;
}
//----------------------------------------------------------------------------
//! Test the deletion of a file to which the delete flag is sent using the
//! Fcntl function on the file object
//----------------------------------------------------------------------------
TEST(FstFileTest, DeleteFlagTest)
{
using namespace XrdCl;
std::unique_ptr mFile(new File());
// Initialize
std::string address = "root://root@" + gEnv->GetMapping("server");
std::string file_path = gEnv->GetMapping("dummy_file");
// Validate URL
URL url(address);
ASSERT_TRUE(url.IsValid());
// Open file
std::string file_url = address + "/" + file_path;
GLOG << "Opening file: " << file_url << std::endl;
XRootDStatus status = mFile->Open(file_url,
OpenFlags::Delete | OpenFlags::Update,
Access::Mode::UR | Access::Mode::UW);
ASSERT_TRUE(status.IsOK());
// Fill buffer with random characters
uint32_t block_size = 4 * 1024;
char* buffer = new char[block_size];
std::ifstream urandom("/dev/urandom", std::ios::in | std::ios::binary);
urandom.read(buffer, block_size);
urandom.close();
// Write data to the file
GLOG << "Performing write operation" << std::endl;
for (int i = 0; i < 10; i++) {
uint64_t offset = (uint64_t) i * block_size;
status = mFile->Write(offset, block_size, buffer);
if (!status.IsOK()) {
std::cerr << "Error while writing at offset: " << offset << std::endl;
std::terminate();
}
}
// Send the delete command using Fcntl
XrdCl::Buffer* response;
XrdCl::Buffer arg;
arg.FromString("delete");
GLOG << "Sending delete command using Fcntl" << std::endl;
status = mFile->Fcntl(arg, response);
ASSERT_TRUE(status.IsOK());
delete response;
// Close the file and then test for its existence
GLOG << "Attempt to reopen deleted file" << std::endl;
status = mFile->Close();
ASSERT_FALSE(status.IsOK());
status = mFile->Open(file_url, OpenFlags::Read, Access::Mode::None);
ASSERT_FALSE(status.IsOK());
delete[] buffer;
}
//------------------------------------------------------------------------------
// Read async test
//------------------------------------------------------------------------------
TEST(FstFileTest, ReadAsyncTest)
{
// Initialize
std::string address = "root://root@" + gEnv->GetMapping("server");
std::string file_path = gEnv->GetMapping("replica_file");
// Validate URL
XrdCl::URL url(address);
ASSERT_TRUE(url.IsValid());
// Open file
std::string file_url = address + "/" + file_path;
GLOG << "Opening file: " << file_url << std::endl;
std::unique_ptr file(new eos::fst::XrdIo(file_url));
ASSERT_EQ(file->fileOpen(SFS_O_RDONLY), 0);
// Get file size
struct stat buff = {0};
ASSERT_EQ(file->fileStat(&buff), 0);
GLOG << "Stat size: " << buff.st_size << std::endl;
uint64_t file_size = (uint64_t) buff.st_size;
off_t buff_size = 4 * 1024;
char* buffer = new char[buff_size];
uint64_t offset = 0;
GLOG << "Performing async read operation" << std::endl;
while (offset < file_size) {
off_t read_size = file->fileReadAsync(offset, buffer, buff_size);
ASSERT_EQ(buff_size, read_size);
offset += buff_size;
}
off_t read_size = file->fileRead(offset, buffer, buff_size);
ASSERT_EQ(read_size, 0);
ASSERT_EQ(file->fileClose(), 0);
delete[] buffer;
}
EOSFSTTEST_NAMESPACE_END