Skip to content
Snippets Groups Projects
Commit b3329153 authored by Eric Cano's avatar Eric Cano
Browse files

Added skelton of a tape device file.

Renames system directory to System
parent 4a814cf6
Branches
Tags
No related merge requests found
......@@ -61,7 +61,7 @@ set(CTEST_OUTPUT_ON_FAILURE 1)
# Add parts first in dependency order
add_subdirectory(SCSI)
add_subdirectory(Drive)
add_subdirectory(system)
add_subdirectory(System)
add_subdirectory(Exception)
add_subdirectory(Utils)
......
add_library(System Wrapper.cc FileWrappers.cc)
// ----------------------------------------------------------------------
// File: System/FileWrapper.cc
// Author: Eric Cano - CERN
// ----------------------------------------------------------------------
/************************************************************************
* Tape Server *
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.*
************************************************************************/
#include <errno.h>
#include <stddef.h>
#include <stdexcept>
#include <sys/mtio.h>
#include "FileWrappers.hh"
int Tape::System::regularFile::ioctl(unsigned long int request, mtget* mt_status)
{
/* Standard "no such operation" reply. */
errno = EINVAL;
return -1;
}
ssize_t Tape::System::regularFile::read(void* buf, size_t nbytes)
{
try {
ssize_t ret;
ret = m_content.copy((char *) buf, nbytes, m_read_pointer);
m_read_pointer += ret;
return ret;
} catch (std::out_of_range & e) {
return 0;
}
}
/**
* Constructor for fake tape server: fill up status registers.
*/
Tape::System::stDeviceFile::stDeviceFile()
{
m_mtStat.mt_type = 1;
m_mtStat.mt_resid = 0;
m_mtStat.mt_dsreg = (((256 * 0x400) & MT_ST_BLKSIZE_MASK) << MT_ST_BLKSIZE_SHIFT)
| ((1 & MT_ST_DENSITY_MASK) << MT_ST_DENSITY_SHIFT);
m_mtStat.mt_gstat = GMT_EOT(~0) | GMT_BOT(~0);
}
ssize_t Tape::System::stDeviceFile::read(void* buf, size_t nbytes)
{
errno = EIO;
return -1;
}
int Tape::System::stDeviceFile::ioctl(unsigned long int request, mtget* mt_status)
{
switch (request) {
case MTIOCGET:
*mt_status = m_mtStat;
return 0;
default:
errno = EINVAL;
return -1;
}
}
// ----------------------------------------------------------------------
// File: System/FileWrapper.hh
// Author: Eric Cano - CERN
// ----------------------------------------------------------------------
/************************************************************************
* Tape Server *
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.*
************************************************************************/
#pragma once
#include <string>
#include <sys/types.h>
#include <sys/mtio.h>
namespace Tape {
namespace System {
/**
* An abstract class allowing simple open/read/close/ioctl interface simlating
* different types of files (regular files, device files (like tape devices)
*/
class vfsFile {
public:
virtual ~vfsFile() {};
virtual ssize_t read(void* buf, size_t nbytes) = 0;
virtual int ioctl(unsigned long int request, struct mtget * mt_status) = 0;
/** Reset the read/write pointers at open. With this, we can use the trivial copy operator. */
virtual void reset() = 0;
};
/**
* Class representing real files
*/
class regularFile: public vfsFile {
public:
regularFile(): m_read_pointer(0) {};
regularFile(const std::string & c): m_content(c), m_read_pointer(0) {};
virtual void reset() { m_read_pointer = 0; };
void operator = (const std::string & s) { m_content = s; m_read_pointer = 0; }
virtual ssize_t read(void* buf, size_t nbytes);
virtual int ioctl(unsigned long int request, struct mtget * mt_status);
private:
std::string m_content;
int m_read_pointer;
};
/**
* Class representing a tape device
*/
class stDeviceFile : public vfsFile {
public:
stDeviceFile();
virtual void reset() {};
virtual ssize_t read(void* buf, size_t nbytes);
virtual int ioctl(unsigned long int request, struct mtget * mt_status);
private:
struct mtget m_mtStat;
};
} // namespace System
} // namespace Tape
// ----------------------------------------------------------------------
// File: System/Wrapper.hh
// Author: Eric Cano - CERN
// ----------------------------------------------------------------------
/************************************************************************
* Tape Server *
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.*
************************************************************************/
#include "Wrapper.hh"
#include <errno.h>
#include <limits.h>
#include <stdexcept>
using ::testing::_;
using ::testing::Invoke;
DIR* Tape::System::fakeWrapper::opendir(const char* name) {
/* Manage absence of directory */
if (m_directories.end() == m_directories.find(std::string(name))) {
errno = ENOENT;
return NULL;
}
/* Dirty pointer gymnastics. Good enough for a test harness */
ourDIR * dir = new ourDIR;
dir->nextIdx = 0;
dir->path = name;
return (DIR*) dir;
}
int Tape::System::fakeWrapper::closedir(DIR* dirp) {
delete ((ourDIR *) dirp);
return 0;
}
struct dirent * Tape::System::fakeWrapper::readdir(DIR* dirp) {
/* Dirty pointer gymnastics. Good enough for a test harness */
ourDIR & dir = *((ourDIR *) dirp);
/* Check we did not reach end of directory. This will create a new
* entry in the map if it does not exist, but we should be protected by
* opendir.
*/
if (dir.nextIdx + 1 > m_directories[dir.path].size())
return NULL;
dir.dent_name = m_directories[dir.path][dir.nextIdx++];
strncpy(dir.dent.d_name, dir.dent_name.c_str(), NAME_MAX);
return & (dir.dent);
}
int Tape::System::fakeWrapper::readlink(const char* path, char* buf, size_t len) {
/*
* Mimic readlink. see man 3 readlink.
*/
if (m_links.end() == m_links.find(std::string(path))) {
errno = ENOENT;
return -1;
}
const std::string & link = m_links[std::string(path)];
strncpy(buf, link.c_str(), len);
return len > link.size() ? link.size() : len;
}
char * Tape::System::fakeWrapper::realpath(const char* name, char* resolved) {
/*
* Mimic realpath. see man 3 realpath.
*/
if (m_realpathes.end() == m_realpathes.find(std::string(name))) {
errno = ENOENT;
return NULL;
}
strncpy(resolved, m_realpathes[std::string(name)].c_str(), PATH_MAX);
return resolved;
}
int Tape::System::fakeWrapper::open(const char* file, int oflag) {
/*
* Mimic open. See man 2 open.
* We only allow read for the moment.
*/
if (oflag & (O_APPEND | O_CREAT)) {
errno = EACCES;
return -1;
}
if (m_files.end() == m_files.find(std::string(file))) {
errno = ENOENT;
return -1;
}
int ret = m_nextFD++;
m_openFiles[ret] = m_files[std::string(file)];
m_openFiles[ret]->reset();
return ret;
}
ssize_t Tape::System::fakeWrapper::read(int fd, void* buf, size_t nbytes) {
/*
* Mimic read. See man 2 read
*/
if (m_openFiles.end() == m_openFiles.find(fd)) {
errno = EBADF;
return -1;
}
return m_openFiles[fd]->read(buf, nbytes);
}
int Tape::System::fakeWrapper::ioctl(int fd, unsigned long int request, mtget* mt_status) {
/*
* Mimic ioctl. Actually delegate the job to a vfsFile
*/
if (m_openFiles.end() == m_openFiles.find(fd)) {
errno = EBADF;
return -1;
}
return m_openFiles[fd]->ioctl(request, mt_status);
}
int Tape::System::fakeWrapper::close(int fd) {
/*
* Mimic close. See man 2 close
*/
if (m_openFiles.end() == m_openFiles.find(fd)) {
errno = EBADF;
return -1;
}
m_openFiles.erase(fd);
return 0;
}
int Tape::System::fakeWrapper::stat(const char* path, struct stat* buf) {
/*
* Mimic stat. See man 2 stat
*/
if (m_stats.end() == m_stats.find(std::string(path))) {
errno = ENOENT;
return -1;
}
*buf = m_stats[std::string(path)];
return 0;
}
/**
* Function merging all types of files into a single pointer
* based map. This allows usage of polymorphic
*/
void Tape::System::fakeWrapper::referenceFiles() {
for (std::map<std::string, regularFile>::iterator i = m_regularFiles.begin();
i != m_regularFiles.end(); i++) {
m_files[i->first] = &m_regularFiles[i->first];
}
for (std::map<std::string, stDeviceFile>::iterator i = m_stFiles.begin();
i != m_stFiles.end(); i++) {
m_files[i->first] = &m_stFiles[i->first];
}
}
void Tape::System::mockWrapper::delegateToFake() {
ON_CALL(*this, opendir(_)).WillByDefault(Invoke(&fake, &fakeWrapper::opendir));
ON_CALL(*this, readdir(_)).WillByDefault(Invoke(&fake, &fakeWrapper::readdir));
ON_CALL(*this, closedir(_)).WillByDefault(Invoke(&fake, &fakeWrapper::closedir));
ON_CALL(*this, readlink(_, _, _)).WillByDefault(Invoke(&fake, &fakeWrapper::readlink));
ON_CALL(*this, realpath(_, _)).WillByDefault(Invoke(&fake, &fakeWrapper::realpath));
ON_CALL(*this, open(_, _)).WillByDefault(Invoke(&fake, &fakeWrapper::open));
ON_CALL(*this, read(_, _, _)).WillByDefault(Invoke(&fake, &fakeWrapper::read));
ON_CALL(*this, ioctl(_, _, _)).WillByDefault(Invoke(&fake, &fakeWrapper::ioctl));
ON_CALL(*this, close(_)).WillByDefault(Invoke(&fake, &fakeWrapper::close));
ON_CALL(*this, stat(_, _)).WillByDefault(Invoke(&fake, &fakeWrapper::stat));
}
void Tape::System::fakeWrapper::setupSLC5() {
/*
* Setup an tree similar to what we'll find in
* and SLC5 system with mvhtl library (one media exchanger, 2 drives)
*/
/*
* First of, the description of all devices in sysfs.
* In SLC5, sysfs is mounted on /sys/. If other mount point appear in the
* future, we'll have to provide /proc/mounts (and use it).
* SLC6 is similar, so this is not necessary at the time of writing.
*/
m_directories["/sys/bus/scsi/devices"].push_back("3:0:0:0");
m_directories["/sys/bus/scsi/devices"].push_back("3:0:1:0");
m_directories["/sys/bus/scsi/devices"].push_back("3:0:2:0");
m_realpathes["/sys/bus/scsi/devices/3:0:0:0"]
= "/sys/devices/pseudo_0/adapter0/host3/target3:0:0/3:0:0:0";
m_realpathes["/sys/bus/scsi/devices/3:0:1:0"]
= "/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0";
m_realpathes["/sys/bus/scsi/devices/3:0:2:0"]
= "/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0";
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:0/3:0:0:0/type"] = "8\n";
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0/type"] = "1\n";
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0/type"] = "1\n";
m_links["/sys/devices/pseudo_0/adapter0/host3/target3:0:0/3:0:0:0/generic"]
= "../../../../../../class/scsi_generic/sg2";
m_links["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0/generic"]
= "../../../../../../class/scsi_generic/sg0";
m_links["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0/generic"]
= "../../../../../../class/scsi_generic/sg1";
m_stats["/dev/sg0"].st_rdev = makedev(21, 0);
m_stats["/dev/sg0"].st_mode = S_IFCHR;
m_stats["/dev/sg1"].st_rdev = makedev(21, 1);
m_stats["/dev/sg1"].st_mode = S_IFCHR;
m_stats["/dev/sg2"].st_rdev = makedev(21, 2);
m_stats["/dev/sg2"].st_mode = S_IFCHR;
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:0/3:0:0:0/generic/dev"] = "21:2\n";
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0/generic/dev"] = "21:0\n";
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0/generic/dev"] = "21:1\n";
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("bus");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("delete");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("device_blocked");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("driver");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("generic");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("iocounterbits");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("iodone_cnt");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("ioerr_cnt");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("iorequest_cnt");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("model");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("power");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("queue_depth");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("queue_type");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("rescan");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("rev");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_device:3:0:1:0");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_generic:sg0");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_level");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_tape:nst0");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_tape:nst0a");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_tape:nst0l");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_tape:nst0m");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_tape:st0");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_tape:st0a");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_tape:st0l");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("scsi_tape:st0m");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("state");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("subsystem");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("tape");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("timeout");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("type");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("uevent");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0"].push_back("vendor");
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0/scsi_tape:st0/dev"] =
"9:0\n";
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:1/3:0:1:0/scsi_tape:nst0/dev"] =
"9:128\n";
m_stats["/dev/st0"].st_rdev = makedev(9, 0);
m_stats["/dev/st0"].st_mode = S_IFCHR;
m_stats["/dev/nst0"].st_rdev = makedev(9, 128);
m_stats["/dev/nst0"].st_mode = S_IFCHR;
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("bus");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("delete");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("device_blocked");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("driver");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("generic");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("iocounterbits");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("iodone_cnt");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("ioerr_cnt");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("iorequest_cnt");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("model");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("power");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("queue_depth");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("queue_type");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("rescan");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("rev");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_device:3:0:2:0");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_generic:sg1s");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_level");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_tape:nst1");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_tape:nst1a");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_tape:nst1l");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_tape:nst1m");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_tape:st1");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_tape:st1a");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_tape:st1l");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("scsi_tape:st1m");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("state");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("subsystem");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("tape");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("timeout");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("type");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("uevent");
m_directories["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0"].push_back("vendor");
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0/scsi_tape:st1/dev"] =
"9:1\n";
m_regularFiles["/sys/devices/pseudo_0/adapter0/host3/target3:0:2/3:0:2:0/scsi_tape:nst1/dev"] =
"9:129\n";
m_stats["/dev/st1"].st_rdev = makedev(9, 1);
m_stats["/dev/st1"].st_mode = S_IFCHR;
m_stats["/dev/nst1"].st_rdev = makedev(9, 129);
m_stats["/dev/nst1"].st_mode = S_IFCHR;
m_stFiles["/dev/st0"];
m_stFiles["/dev/st1"];
referenceFiles();
}
// ----------------------------------------------------------------------
// File: System/Wrapper.hh
// Author: Eric Cano - CERN
// ----------------------------------------------------------------------
/************************************************************************
* Tape Server *
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.*
************************************************************************/
#pragma once
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
#include <gmock/gmock.h>
#include <map>
#include <vector>
#include <string>
#include <fstream>
#include "FileWrappers.hh"
namespace Tape {
namespace System {
/**
* Wrapper class the all system calls used, allowing writing of test harnesses
* for unit testing.
* The member functions are purposedly non-virtual, allowing full
* performance with inline member functions.
*/
class realWrapper {
public:
DIR* opendir(const char *name) { return ::opendir(name); }
struct dirent * readdir(DIR* dirp) { return ::readdir(dirp); }
int closedir(DIR* dirp) { return ::closedir(dirp); }
int readlink(const char* path, char* buf, size_t len) { return ::readlink(path, buf, len); }
char * realpath(const char* name, char* resolved) { return ::realpath(name, resolved); }
int open(const char* file, int oflag) { return ::open(file, oflag); }
int ioctl(int fd, unsigned long int request, struct mtget * mt_status) {
::ioctl(fd, request, mt_status);
}
ssize_t read(int fd, void* buf, size_t nbytes) { return ::read(fd, buf, nbytes); }
int close(int fd) { return ::close(fd); }
int stat(const char * path, struct stat *buf) { return ::stat(path, buf); }
};
/**
* Intermediate class definition, allowing common ancestor between
* mockWrapper and fakeWrapper (pure virtual)
*/
class virtualWrapper {
public:
virtual DIR* opendir(const char *name) = 0;
virtual struct dirent * readdir(DIR* dirp) = 0;
virtual int closedir(DIR* dirp) = 0;
virtual int readlink(const char* path, char* buf, size_t len) = 0;
virtual char * realpath(const char* name, char* resolved) = 0;
virtual int open(const char* file, int oflag) = 0;
virtual ssize_t read(int fd, void* buf, size_t nbytes) = 0;
/* The ... (variable arguments) notation will not work with GMock.
* We have to create one overload for each case we encounter. */
virtual int ioctl(int fd, unsigned long int request, struct mtget * mt_status) = 0;
virtual int close(int fd) = 0;
virtual int stat(const char * path, struct stat *buf) = 0;
};
/**
* Fake class for system wrapper. Allows recording of pre-cooked filesystem elements,
* once for each call separately.
* Each test can then delegate (from mock) and configure
*/
class fakeWrapper : public virtualWrapper {
public:
fakeWrapper() : m_nextFD(0) {
};
virtual DIR* opendir(const char *name);
virtual struct dirent * readdir(DIR* dirp);
virtual int closedir(DIR* dirp);
virtual int readlink(const char* path, char* buf, size_t len);
virtual char * realpath(const char* name, char* resolved);
virtual int open(const char* file, int oflag);
virtual int ioctl(int fd, unsigned long int request, struct mtget * mt_status);
virtual ssize_t read(int fd, void* buf, size_t nbytes);
virtual int close(int fd);
virtual int stat(const char * path, struct stat *buf);
std::map<std::string, std::vector<std::string> > m_directories;
std::map<std::string, std::string> m_links;
std::map<std::string, std::string> m_realpathes;
std::map<std::string, vfsFile *> m_files;
std::map<std::string, struct stat> m_stats;
std::map<std::string, regularFile> m_regularFiles;
std::map<std::string, stDeviceFile> m_stFiles;
void setupSLC5();
void referenceFiles();
private:
struct ourDIR {
std::string path;
int nextIdx;
struct dirent dent;
std::string dent_name;
};
std::map<int, vfsFile *> m_openFiles;
int m_nextFD;
};
/**
* Mock class for system wrapper, used to develop tests.
*/
class mockWrapper : public virtualWrapper {
public:
mockWrapper() : m_DIR((DIR*) & m_DIRfake) {
ON_CALL(*this, opendir(::testing::_))
.WillByDefault(::testing::Return(m_DIR));
}
MOCK_METHOD1(opendir, DIR*(const char *name));
MOCK_METHOD1(readdir, dirent*(DIR* dirp));
MOCK_METHOD1(closedir, int(DIR* dirp));
MOCK_METHOD3(readlink, int(const char* path, char* buf, size_t len));
MOCK_METHOD2(realpath, char *(const char* name, char* resolved));
MOCK_METHOD2(open, int(const char* file, int oflag));
MOCK_METHOD3(read, ssize_t(int fd, void* buf, size_t nbytes));
MOCK_METHOD3(ioctl, int(int fd, unsigned long int request, struct mtget * mt_status));
MOCK_METHOD1(close, int(int fd));
MOCK_METHOD2(stat, int(const char *, struct stat *));
DIR* m_DIR;
int m_DIRfake;
void delegateToFake();
fakeWrapper fake;
};
} // namespace System
} // namespace Tape
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment