file: Open and close files

- Support OPEN_READ and OPEN_WRITE
- Update the design to accomidate for error checking
- Add lib/test.cpp containing basic functions that can be used for
  testing

Signed-off-by: Bryan Schumaker <bjschuma@gmail.com>
This commit is contained in:
Bryan Schumaker 2013-07-28 19:57:07 -04:00 committed by Anna Schumaker
parent e0a32c10ac
commit ecd56831f6
11 changed files with 341 additions and 33 deletions

View File

@ -161,24 +161,27 @@ On-disk files: (lib/file.cpp)
enum OpenMode {
OPEN_READ,
OPEN_WRITE,
NOT_OPEN,
}
- File:
class File {
private:
union {
ifstream in;
ofstream out;
} f;
ifstream in;
ofstream out;
unsigned int version;
OpenMode mode;
FileLocHint hint;
string filepath;
public:
File(string, FileLocHint);
~File();
const char *get_filepath();
bool exists();
bool open(OpenMode);
bool close();
const File &operator<<(File &);
const File &operator>>(File &);
@ -198,6 +201,9 @@ On-disk files: (lib/file.cpp)
$HOME/.ocarina/
$HOME/.ocarina-debug/
File : ~File()
Close the file stream if it is open.
const char *get_filepath()
Return the full filepath to the file.
@ -218,7 +224,11 @@ On-disk files: (lib/file.cpp)
- Write version information to the start of the file
- Return true
Return false if there are any errors opening the file.
Return false if the file is already open.
Return false if there are any other errors.
void File : close()
Close a file after IO.
File : operator<<()
Write data to the file.

View File

@ -42,24 +42,27 @@ On-disk files: (lib/file.cpp)
enum OpenMode {
OPEN_READ,
OPEN_WRITE,
NOT_OPEN,
}
- File:
class File {
private:
union {
ifstream in;
ofstream out;
} f;
ifstream in;
ofstream out;
unsigned int version;
OpenMode mode;
FileLocHint hint;
string filepath;
public:
File(string, FileLocHint);
~File();
const char *get_filepath();
bool exists();
bool open(OpenMode);
bool close();
const File &operator<<(File &);
const File &operator>>(File &);
@ -79,6 +82,9 @@ On-disk files: (lib/file.cpp)
$HOME/.ocarina/
$HOME/.ocarina-debug/
File : ~File()
Close the file stream if it is open.
const char *get_filepath()
Return the full filepath to the file.
@ -99,7 +105,11 @@ On-disk files: (lib/file.cpp)
- Write version information to the start of the file
- Return true
Return false if there are any errors opening the file.
Return false if the file is already open.
Return false if there are any other errors.
void File : close()
Close a file after IO.
File : operator<<()
Write data to the file.

View File

@ -4,22 +4,42 @@
#ifndef OCARINA_FILE_H
#define OCARINA_FILE_H
#include <fstream>
#include <string>
#define FILE_VERSION 0
enum FileLocHint {
FILE_TYPE_CONFIG,
FILE_TYPE_DATA,
FILE_TYPE_LEGACY,
};
enum OpenMode {
OPEN_READ,
OPEN_WRITE,
NOT_OPEN,
};
class File {
private:
std::fstream stream;
OpenMode mode;
FileLocHint hint;
std::string filepath;
unsigned int version;
void find_dir(std::string &);
bool open_read();
bool open_write();
public:
File(std::string, FileLocHint);
~File();
const char *get_filepath();
bool exists();
bool open(OpenMode);
bool close();
};
#endif /* OCARINA_FILE_H */

9
include/test.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef OCARINA_TEST_H
#define OCARINA_TEST_H
#ifdef CONFIG_TEST
void rm_test_config();
void rm_test_data();
#endif /* CONFIG_TEST */
#endif /* OCARINA_TEST_H */

View File

@ -7,4 +7,7 @@ if CONFIG.FILE:
CONFIG.package("glib-2.0")
build += [ env.Object("file.cpp") ]
if CONFIG.TEST:
build += [ env.Object("test.cpp") ]
Return("build")

View File

@ -2,6 +2,7 @@
* Copyright 2013 (c) Bryan Schumaker.
*/
#include <file.h>
#include <print.h>
#include <glib.h>
@ -13,37 +14,118 @@
#define OCARINA_DIR "ocarina"
#endif
void File :: find_dir(std::string &res)
{
switch (hint) {
case FILE_TYPE_CONFIG:
res = g_get_user_config_dir();
break;
case FILE_TYPE_DATA:
res = g_get_user_data_dir();
break;
case FILE_TYPE_LEGACY:
res = g_get_home_dir();
}
res += "/";
switch (hint) {
case FILE_TYPE_CONFIG:
case FILE_TYPE_DATA:
res += OCARINA_DIR;
break;
case FILE_TYPE_LEGACY:
res += ".";
res += OCARINA_DIR;
}
}
File :: File(std::string path, FileLocHint file_hint)
: hint(file_hint)
: mode(NOT_OPEN), hint(file_hint), version(FILE_VERSION)
{
std::string dir;
switch (file_hint) {
case FILE_TYPE_CONFIG:
dir = g_get_user_config_dir();
break;
case FILE_TYPE_DATA:
dir = g_get_user_data_dir();
break;
case FILE_TYPE_LEGACY:
dir = g_get_home_dir();
}
dir += "/";
switch (file_hint) {
case FILE_TYPE_CONFIG:
case FILE_TYPE_DATA:
dir += OCARINA_DIR;
break;
case FILE_TYPE_LEGACY:
dir += ".";
dir += OCARINA_DIR;
}
find_dir(dir);
filepath = dir + "/" + path;
}
File :: ~File()
{
close();
}
const char *File :: get_filepath()
{
return filepath.c_str();
}
bool File :: exists()
{
return g_file_test(filepath.c_str(), G_FILE_TEST_EXISTS);
}
bool File :: open_read()
{
if (!exists()) {
dprint("ERROR: File does not exist (%s)\n", filepath.c_str());
return false;
}
stream.open(filepath.c_str(), std::fstream::in);
if (stream.fail()) {
dprint("ERROR: Could not open file for reading (%s)\n", filepath.c_str());
return false;
}
mode = OPEN_READ;
stream >> version;
return stream.good();
}
bool File :: open_write()
{
std::string dir;
if (hint == FILE_TYPE_LEGACY) {
dprint("ERROR: Cannot write to legacy files (%s)\n", filepath.c_str());
return false;
}
find_dir(dir);
if (g_mkdir_with_parents(dir.c_str(), 0755) != 0) {
dprint("ERROR: Could not make directory (%s)\n", dir.c_str());
return false;
}
stream.open(filepath.c_str(), std::fstream::out);
if (stream.fail()) {
dprint("ERROR: Could not open file for writing (%s)\n", filepath.c_str());
return false;
}
mode = OPEN_WRITE;
stream << version << std::endl;
return stream.good();
}
bool File :: open(OpenMode m)
{
if (mode != NOT_OPEN) {
dprint("ERROR: File is already open (%s)\n", filepath.c_str());
return false;
}
if (m == NOT_OPEN) {
dprint("ERROR: NOT_OPEN is not a legal OpenMode (%s)\n", filepath.c_str());
return false;
} else if (m == OPEN_READ)
return open_read();
else /* m == OPEN_WRITE */
return open_write();
}
bool File :: close()
{
if (mode != NOT_OPEN)
stream.close();
mode = NOT_OPEN;
return stream.good();
}

27
lib/test.cpp Normal file
View File

@ -0,0 +1,27 @@
/*
* Copyright 2013 (c) Bryan Schumaker.
*/
#include <test.h>
#include <string>
#include <glib.h>
#include <stdlib.h>
/* A bit hackish, but it works... */
static void rm_dir(const char *dir)
{
std::string cmd = "rm -frv ";
cmd += dir;
cmd += "/ocarina-test";
system(cmd.c_str());
}
void rm_test_config()
{
rm_dir(g_get_user_config_dir());
}
void rm_test_data()
{
rm_dir(g_get_user_data_dir());
}

View File

@ -4,3 +4,4 @@ Import("Test", "CONFIG")
CONFIG.FILE = True
Test("file", "compile.cpp")
Test("file", "open.cpp")

View File

@ -0,0 +1,22 @@
removed /home/anna/.local/share/ocarina-test/test.4
removed /home/anna/.local/share/ocarina-test/test.3
removed directory: /home/anna/.local/share/ocarina-test
ERROR: NOT_OPEN is not a legal OpenMode (/home/anna/.local/share/ocarina-test/test.0)
Test 0: Passed
ERROR: File does not exist (/home/anna/.local/share/ocarina-test/test.1)
Test 1: Passed
ERROR: Cannot write to legacy files (/home/anna/.ocarina-test/test.2)
Test 2: Passed
Test 3a: Passed
ERROR: File is already open (/home/anna/.local/share/ocarina-test/test.3)
Test 3b: Passed
Test 4a: Passed
Test 4b: Passed
Test 4c: Passed
Test 5: Passed

106
tests/file/open.cpp Normal file
View File

@ -0,0 +1,106 @@
/*
* Test opening files under various conditions
*/
#include <file.h>
#include <test.h>
#include <print.h>
static int test_result(const char *num, bool res, bool expected)
{
print("Test %s: ", num);
if (res == expected) {
print("Passed\n");
return 0;
} else {
print("Failed\n");
return 1;
}
}
static int test_error(const char *num, FileLocHint hint, OpenMode mode)
{
std::string f = "test.";
f += num;
File file(f.c_str(), hint);
print("\n");
return test_result(num, file.open(mode), false);
}
/*
* Attempt to open a file with mode == NOT_OPEN
*/
int test_0()
{
return test_error("0", FILE_TYPE_DATA, NOT_OPEN);
}
/*
* Attempt to open a file that doesn't exist for reading
*/
int test_1()
{
return test_error("1", FILE_TYPE_DATA, OPEN_READ);
}
/*
* Attempt to open a legacy file for writing
*/
int test_2()
{
return test_error("2", FILE_TYPE_LEGACY, OPEN_WRITE);
}
/*
* Write to a file, then open it for reading WITHOUT closing first
*/
int test_3()
{
File file("test.3", FILE_TYPE_DATA);
printf("\n");
if (test_result("3a", file.open(OPEN_WRITE), true) != 0)
return 1;
return test_result("3b", file.open(OPEN_READ), false);
}
/*
* Write to a file, close it, then open it again for reading
*/
int test_4()
{
File file("test.4", FILE_TYPE_DATA);
printf("\n");
if (test_result("4a", file.open(OPEN_WRITE), true) != 0)
return 1;
if (test_result("4b", file.close(), true) != 0)
return 1;
return test_result("4c", file.open(OPEN_READ), true);
}
/*
* Attempt to close a file that was never opened
*/
int test_5()
{
File file("test.5", FILE_TYPE_DATA);
print("\n");
return test_result("5", file.close(), true);
}
int main(int argc, char **argv)
{
int failed = 0;
rm_test_data();
failed += test_0();
failed += test_1();
failed += test_2();
failed += test_3();
failed += test_4();
failed += test_5();
return failed;
}

18
tests/file/open.good Normal file
View File

@ -0,0 +1,18 @@
removed /home/anna/.local/share/ocarina-test/test.4
removed /home/anna/.local/share/ocarina-test/test.3
removed directory: /home/anna/.local/share/ocarina-test
Test 0: Passed
Test 1: Passed
Test 2: Passed
Test 3a: Passed
Test 3b: Passed
Test 4a: Passed
Test 4b: Passed
Test 4c: Passed
Test 5: Passed