file: Update design and create a new unit test
Signed-off-by: Anna Schumaker <schumaker.anna@gmail.com>
This commit is contained in:
parent
3359cbae6f
commit
336024bae9
24
DESIGN
24
DESIGN
|
@ -132,18 +132,13 @@ Callbacks: (lib/callback.cpp)
|
||||||
|
|
||||||
On-disk files: (lib/file.cpp)
|
On-disk files: (lib/file.cpp)
|
||||||
Data will be stored in the user's home directory according to the
|
Data will be stored in the user's home directory according to the
|
||||||
XDG / freedesktop.org specification. This means storing data in
|
XDG / freedesktop.org specification. This means data will be stored
|
||||||
$XDG_DATA_HOME/ocarina{-debug|-test}/ and storing configuration in
|
in a subdirectory of $XDG_DATA_HOME.
|
||||||
$XDG_CONFIG_HOME/ocarina{-debug|-test}/. In addition, I will support
|
|
||||||
importing data from Ocarina 5.10 for conversion to the new format.
|
|
||||||
|
|
||||||
In theory my file format will not change often, so it should be
|
The filse class will support reading and writing files in the users
|
||||||
possible to use multiple Ocarina versions with the same data. However,
|
$XDG_CONFIG_HOME/ocarina{-debug|test}. In addition, Ocarina 6.0 will
|
||||||
should the format need to change I will only support forward
|
support reading library files from the Ocarina 5.10 directory:
|
||||||
compatibility. This means that downgrades will not be possible after
|
$HOME/.ocarina{-debug}.
|
||||||
a file format change. To keep the code even cleaner, I will only
|
|
||||||
support updating from the previous file format version. This means
|
|
||||||
that legacy support will be removed after the first file format change.
|
|
||||||
|
|
||||||
Items should be written to a file with either a space or new line
|
Items should be written to a file with either a space or new line
|
||||||
separating multiple values.
|
separating multiple values.
|
||||||
|
@ -156,7 +151,6 @@ On-disk files: (lib/file.cpp)
|
||||||
|
|
||||||
- Hint where the file is located:
|
- Hint where the file is located:
|
||||||
enum FileLocHint {
|
enum FileLocHint {
|
||||||
FILE_TYPE_CONFIG,
|
|
||||||
FILE_TYPE_DATA,
|
FILE_TYPE_DATA,
|
||||||
FILE_TYPE_LEGACY,
|
FILE_TYPE_LEGACY,
|
||||||
FILE_TYPE_INVALID,
|
FILE_TYPE_INVALID,
|
||||||
|
@ -195,9 +189,9 @@ On-disk files: (lib/file.cpp)
|
||||||
- API:
|
- API:
|
||||||
File :: File(string filepath, FileLocHint hint);
|
File :: File(string filepath, FileLocHint hint);
|
||||||
Resolve filepath to one of:
|
Resolve filepath to one of:
|
||||||
XDG_{CONFIG|DATA}_HOME/ocarina/filepath
|
XDG_DATA_HOME/ocarina/filepath
|
||||||
XDG_{CONFIG|DATA}_HOME/ocarina-debug/filepath
|
XDG_DATA_HOME/ocarina-debug/filepath
|
||||||
XDG_{CONFIG|DATA}_HOME/ocarina-test/filepath
|
XDG_DATA_HOME/ocarina-test/filepath
|
||||||
$HOME/.ocarina/
|
$HOME/.ocarina/
|
||||||
$HOME/.ocarina-debug/
|
$HOME/.ocarina-debug/
|
||||||
$HOME/.ocarina-test/
|
$HOME/.ocarina-test/
|
||||||
|
|
1
config
1
config
|
@ -26,6 +26,7 @@ Export("env", "use_package", "CONFIG_DEBUG", "CONFIG_VERSION")
|
||||||
include = SConscript("include/Sconscript")
|
include = SConscript("include/Sconscript")
|
||||||
|
|
||||||
lib = SConscript("lib/Sconscript")
|
lib = SConscript("lib/Sconscript")
|
||||||
|
Export("lib")
|
||||||
|
|
||||||
tests = SConscript("tests/Sconscript")
|
tests = SConscript("tests/Sconscript")
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#define FILE_VERSION 0
|
#define FILE_VERSION 0
|
||||||
|
|
||||||
enum FileLocHint {
|
enum FileLocHint {
|
||||||
FILE_TYPE_CONFIG,
|
|
||||||
FILE_TYPE_DATA,
|
FILE_TYPE_DATA,
|
||||||
FILE_TYPE_LEGACY,
|
FILE_TYPE_LEGACY,
|
||||||
FILE_TYPE_INVALID,
|
FILE_TYPE_INVALID,
|
||||||
|
@ -29,7 +28,7 @@ private:
|
||||||
std::string filepath;
|
std::string filepath;
|
||||||
unsigned int version;
|
unsigned int version;
|
||||||
|
|
||||||
void find_dir(std::string &);
|
std::string find_dir();
|
||||||
void open_read();
|
void open_read();
|
||||||
void open_write();
|
void open_write();
|
||||||
|
|
||||||
|
|
94
lib/file.cpp
94
lib/file.cpp
|
@ -15,49 +15,16 @@
|
||||||
#define OCARINA_DIR "ocarina"
|
#define OCARINA_DIR "ocarina"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void File :: find_dir(std::string &res)
|
|
||||||
{
|
|
||||||
if (hint == FILE_TYPE_INVALID)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (hint) {
|
|
||||||
case FILE_TYPE_CONFIG:
|
|
||||||
res = g_get_user_config_dir();
|
|
||||||
break;
|
|
||||||
case FILE_TYPE_DATA:
|
|
||||||
res = g_get_user_data_dir();
|
|
||||||
break;
|
|
||||||
default: /* FILE_TYPE_LEGACY */
|
|
||||||
res = g_get_home_dir();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
res += "/";
|
|
||||||
switch (hint) {
|
|
||||||
case FILE_TYPE_CONFIG:
|
|
||||||
case FILE_TYPE_DATA:
|
|
||||||
res += OCARINA_DIR;
|
|
||||||
break;
|
|
||||||
default: /* FILE_TYPE_LEGACY */
|
|
||||||
res += ".";
|
|
||||||
res += OCARINA_DIR;
|
|
||||||
res += "/library";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File :: File(const std::string &path, FileLocHint file_hint)
|
File :: File(const std::string &path, FileLocHint file_hint)
|
||||||
: mode(NOT_OPEN), hint(file_hint)
|
: mode(NOT_OPEN), hint(file_hint), version(FILE_VERSION)
|
||||||
{
|
{
|
||||||
std::string dir;
|
if (path.size() == 0)
|
||||||
|
|
||||||
if (path == "") {
|
|
||||||
hint = FILE_TYPE_INVALID;
|
hint = FILE_TYPE_INVALID;
|
||||||
filepath = "INVALID";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
find_dir(dir);
|
if (hint == FILE_TYPE_INVALID)
|
||||||
filepath = dir + "/" + path;
|
filepath = "INVALID";
|
||||||
|
else
|
||||||
|
filepath = find_dir() + "/" + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
File :: ~File()
|
File :: ~File()
|
||||||
|
@ -80,16 +47,34 @@ bool File :: exists()
|
||||||
return g_file_test(filepath.c_str(), G_FILE_TEST_EXISTS);
|
return g_file_test(filepath.c_str(), G_FILE_TEST_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string File :: find_dir()
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
|
||||||
|
if (hint == FILE_TYPE_DATA) {
|
||||||
|
res = g_get_user_data_dir();
|
||||||
|
res += "/";
|
||||||
|
res += OCARINA_DIR;
|
||||||
|
} else /* FILE_TYPE_LEGACY */ {
|
||||||
|
res = g_get_home_dir();
|
||||||
|
res += "/.";
|
||||||
|
res += OCARINA_DIR;
|
||||||
|
res += "/library";
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void File :: open_read()
|
void File :: open_read()
|
||||||
{
|
{
|
||||||
if (!exists()) {
|
if (!exists()) {
|
||||||
dprint("ERROR: File does not exist (%s)\n", filepath.c_str());
|
dprint("ERROR: File does not exist\n");
|
||||||
throw -E_EXIST;
|
throw -E_EXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::fstream::open(filepath.c_str(), std::fstream::in);
|
std::fstream::open(filepath.c_str(), std::fstream::in);
|
||||||
if (std::fstream::fail()) {
|
if (std::fstream::fail()) {
|
||||||
dprint("ERROR: Could not open file for reading (%s)\n", filepath.c_str());
|
dprint("ERROR: File could not be opened for reading\n");
|
||||||
throw -E_IO;
|
throw -E_IO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,21 +85,20 @@ void File :: open_read()
|
||||||
|
|
||||||
void File :: open_write()
|
void File :: open_write()
|
||||||
{
|
{
|
||||||
std::string dir;
|
|
||||||
if (hint == FILE_TYPE_LEGACY) {
|
if (hint == FILE_TYPE_LEGACY) {
|
||||||
dprint("ERROR: Cannot write to legacy files (%s)\n", filepath.c_str());
|
dprint("ERROR: Cannot write to legacy files\n");
|
||||||
throw -E_IO;
|
throw -E_IO;
|
||||||
}
|
}
|
||||||
|
|
||||||
find_dir(dir);
|
std::string dir = find_dir();
|
||||||
if (g_mkdir_with_parents(dir.c_str(), 0755) != 0) {
|
if (g_mkdir_with_parents(dir.c_str(), 0755) != 0) {
|
||||||
dprint("ERROR: Could not make directory (%s)\n", dir.c_str());
|
dprint("ERROR: Could not make directory\n");
|
||||||
throw -E_IO;
|
throw -E_IO;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::fstream::open(filepath.c_str(), std::fstream::out);
|
std::fstream::open(filepath.c_str(), std::fstream::out);
|
||||||
if (std::fstream::fail()) {
|
if (std::fstream::fail()) {
|
||||||
dprint("ERROR: Could not open file for writing (%s)\n", filepath.c_str());
|
dprint("ERROR: Could not open file for writing\n");
|
||||||
throw -E_IO;
|
throw -E_IO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,18 +108,18 @@ void File :: open_write()
|
||||||
|
|
||||||
void File :: open(OpenMode m)
|
void File :: open(OpenMode m)
|
||||||
{
|
{
|
||||||
if (mode != NOT_OPEN) {
|
if (hint == FILE_TYPE_INVALID) {
|
||||||
dprint("ERROR: File is already open (%s)\n", filepath.c_str());
|
dprint("ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened\n");
|
||||||
|
throw -E_INVAL;
|
||||||
|
} else if (m == NOT_OPEN) {
|
||||||
|
dprint("ERROR: NOT_OPEN is not a legal OpenMode\n");
|
||||||
|
throw -E_INVAL;
|
||||||
|
} else if (mode != NOT_OPEN) {
|
||||||
|
dprint("ERROR: File is already open\n");
|
||||||
throw -E_IO;
|
throw -E_IO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m == NOT_OPEN) {
|
else if (m == OPEN_READ)
|
||||||
dprint("ERROR: NOT_OPEN is not a legal OpenMode (%s)\n", filepath.c_str());
|
|
||||||
throw -E_INVAL;
|
|
||||||
} else if (hint == FILE_TYPE_INVALID) {
|
|
||||||
dprint("ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened\n");
|
|
||||||
throw -E_INVAL;
|
|
||||||
} else if (m == OPEN_READ)
|
|
||||||
open_read();
|
open_read();
|
||||||
else /* m == OPEN_WRITE */
|
else /* m == OPEN_WRITE */
|
||||||
open_write();
|
open_write();
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
|
env.Append( CCFLAGS = [ "-DCONFIG_TEST" ] )
|
||||||
src = SConscript("src/Sconscript")
|
src = SConscript("src/Sconscript")
|
||||||
|
|
||||||
tests = [ "version" ]
|
tests = [ "version", "file" ]
|
||||||
|
|
||||||
|
|
||||||
def Test(name):
|
|
||||||
return Command("%s.out" % name, [], "./tests/%s" % name)
|
|
||||||
|
|
||||||
for test in tests:
|
|
||||||
t = Test(test)
|
|
||||||
Depends(t, src)
|
|
||||||
AlwaysBuild(t)
|
|
||||||
|
|
||||||
#AlwaysBuild(test)
|
|
||||||
#Depends(test, src)
|
|
||||||
|
|
||||||
#scripts = [ "print", "file", "database", "index", "filter", "idle", "playlist",
|
#scripts = [ "print", "file", "database", "index", "filter", "idle", "playlist",
|
||||||
# "library", "playqueue", "deck", "audio", "gui" ]
|
# "library", "playqueue", "deck", "audio", "gui" ]
|
||||||
|
|
||||||
|
prev = None
|
||||||
|
|
||||||
|
for test in tests:
|
||||||
|
t = Command("%s.out" % test, [], "./tests/%s" % test)
|
||||||
|
|
||||||
|
if prev:
|
||||||
|
Depends(t, prev)
|
||||||
|
Depends(t, src)
|
||||||
|
AlwaysBuild(t)
|
||||||
|
prev = t
|
||||||
|
|
|
@ -16,18 +16,51 @@ function config_debug
|
||||||
read_config CONFIG_DEBUG
|
read_config CONFIG_DEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_success
|
CUR_TEST=0
|
||||||
|
function new_test
|
||||||
{
|
{
|
||||||
echo "Success!"
|
echo "$1"
|
||||||
exit 0
|
CUR_TEST=0
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_failed
|
function start_test
|
||||||
{
|
{
|
||||||
echo "FAILED =("
|
echo -n " $CUR_TEST: "
|
||||||
on_failed
|
let CUR_TEST=($CUR_TEST + 1)
|
||||||
exit 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assert_equal
|
||||||
|
{
|
||||||
|
if [ "$1" == "$2" ]; then
|
||||||
|
echo "Success!"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "FAILED =("
|
||||||
|
echo " Expected: $2"
|
||||||
|
echo " Actual: $1"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_equal
|
||||||
|
{
|
||||||
|
start_test
|
||||||
|
assert_equal "$($1)" "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
function on_exit
|
||||||
|
{
|
||||||
|
ret=$?
|
||||||
|
echo
|
||||||
|
return $ret
|
||||||
|
}; trap "on_exit" EXIT
|
||||||
|
|
||||||
|
[ -z $HOME ] && HOME=$(cat /etc/passwd | grep $(whoami) | awk -F: '{print $6}')
|
||||||
|
[ -z $XDG_DATA_HOME] && XDG_DATA_HOME="$HOME/.local/share"
|
||||||
|
DATA_DIR="$XDG_DATA_HOME/ocarina-test"
|
||||||
|
LEGACY_DIR="$HOME/.ocarina-test/library"
|
||||||
|
|
||||||
|
rm -rf $DATA_DIR 2>/dev/null || true
|
||||||
|
|
||||||
|
set -e
|
||||||
cd $(dirname $0)
|
cd $(dirname $0)
|
||||||
echo -n "Testing $(basename $0) ... "
|
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Copyright 2014 (c) Anna Schumaker
|
||||||
|
|
||||||
|
. $(dirname $0)/_functions
|
||||||
|
|
||||||
|
function test_file
|
||||||
|
{
|
||||||
|
test_equal "./src/file $1" "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# Test filepaths
|
||||||
|
#
|
||||||
|
|
||||||
|
new_test "Filepath Test"
|
||||||
|
|
||||||
|
test_file -D INVALID
|
||||||
|
test_file -L INVALID
|
||||||
|
test_file "-D file.txt" "$DATA_DIR/file.txt"
|
||||||
|
test_file "-L file.txt" "$LEGACY_DIR/file.txt"
|
||||||
|
test_file "file.txt" INVALID
|
||||||
|
test_file "-D -v file.txt" "0"
|
||||||
|
test_file "-L -v file.txt" "0"
|
||||||
|
|
||||||
|
if [ -d $DATA_DIR ]; then
|
||||||
|
echo "ERROR: $DATA_DIR should not exist!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# Test opening files
|
||||||
|
#
|
||||||
|
|
||||||
|
echo
|
||||||
|
new_test "File Open Test"
|
||||||
|
|
||||||
|
# Generic open testing
|
||||||
|
test_file "-o N file.txt" "ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened"
|
||||||
|
test_file "-D -o N file.txt" "ERROR: NOT_OPEN is not a legal OpenMode"
|
||||||
|
test_file "-D -o W -O file.txt" "ERROR: File is already open" # This test creates a file
|
||||||
|
test_file "-D -o R -O file.txt" "ERROR: File is already open"
|
||||||
|
rm $DATA_DIR/*
|
||||||
|
|
||||||
|
# Test opening for read
|
||||||
|
test_file "-D -o R file.txt" "ERROR: File does not exist"
|
||||||
|
|
||||||
|
touch $DATA_DIR/file.txt
|
||||||
|
chmod -r $DATA_DIR/file.txt
|
||||||
|
test_file "-D -o R file.txt" "ERROR: File could not be opened for reading"
|
||||||
|
rm -r $DATA_DIR
|
||||||
|
|
||||||
|
# Test opening for write
|
||||||
|
test_file "-L -o W file.txt" "ERROR: Cannot write to legacy files"
|
||||||
|
|
||||||
|
touch $DATA_DIR
|
||||||
|
test_file "-D -o W file.txt" "ERROR: Could not make directory"
|
||||||
|
|
||||||
|
rm $DATA_DIR
|
||||||
|
mkdir -p $DATA_DIR
|
||||||
|
touch $DATA_DIR/file.txt
|
||||||
|
chmod -w $DATA_DIR/file.txt
|
||||||
|
test_file "-D -o W file.txt" "ERROR: Could not open file for writing"
|
||||||
|
rm -rf $DATA_DIR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# Test closing files
|
||||||
|
#
|
||||||
|
|
||||||
|
echo
|
||||||
|
new_test "File Close Test"
|
||||||
|
test_file "-D -c file.txt" ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# Test FILE IO
|
||||||
|
#
|
||||||
|
|
||||||
|
data="ABCDE FGHIJ KLMNO PQRST UVWXYZ"
|
||||||
|
echo
|
||||||
|
new_test "File IO Test"
|
||||||
|
|
||||||
|
# Write to file
|
||||||
|
./src/file -D -w file.txt "$data"
|
||||||
|
start_test
|
||||||
|
assert_equal "$(cat $DATA_DIR/file.txt)" "0
|
||||||
|
$data"
|
||||||
|
|
||||||
|
# Read data back from file
|
||||||
|
test_file "-D -r file.txt" "ABCDE
|
||||||
|
FGHIJ
|
||||||
|
KLMNO
|
||||||
|
PQRST
|
||||||
|
UVWXYZ"
|
||||||
|
|
||||||
|
# Write different data to file
|
||||||
|
./src/file -D -w file.txt " $data"
|
||||||
|
start_test
|
||||||
|
assert_equal "$(cat $DATA_DIR/file.txt)" "0
|
||||||
|
$data"
|
||||||
|
|
||||||
|
# Read data back in a single line
|
||||||
|
test_file "-D -r -g file.txt" "$data"
|
|
@ -1,6 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
Import("Test", "CONFIG")
|
|
||||||
|
|
||||||
CONFIG.FILE = True
|
|
||||||
|
|
||||||
Test("file", "file.cpp")
|
|
|
@ -1,287 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2013 (c) Anna Schumaker.
|
|
||||||
*/
|
|
||||||
#include <error.h>
|
|
||||||
#include <file.h>
|
|
||||||
#include <print.h>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
bool test_result(bool passed, bool print_spaces)
|
|
||||||
{
|
|
||||||
if (print_spaces)
|
|
||||||
print(" ");
|
|
||||||
if (passed == true)
|
|
||||||
print("Passed\n");
|
|
||||||
else
|
|
||||||
print("Failed\n");
|
|
||||||
return passed;
|
|
||||||
}
|
|
||||||
|
|
||||||
File *get_file(int testno, char subtest, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
std::stringstream s;
|
|
||||||
std::string path = "";
|
|
||||||
if (valid == true) {
|
|
||||||
path = "file.";
|
|
||||||
s << testno;
|
|
||||||
path += s.str() + subtest;
|
|
||||||
}
|
|
||||||
print("Test %d%c: ", testno, subtest);
|
|
||||||
return new File(path, hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool inval_or_legacy(bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
return (valid == false) || (hint == FILE_TYPE_LEGACY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check how filepath is expanded
|
|
||||||
* An empty filepath == INVALID
|
|
||||||
*/
|
|
||||||
void test_a(int testno, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
File *f = get_file(testno, 'a', valid, hint);
|
|
||||||
print("File path is %s\n", f->get_filepath());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NOT_OPEN is not a valid openmode
|
|
||||||
*/
|
|
||||||
void test_b(int testno, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
File *f = get_file(testno, 'b', valid, hint);
|
|
||||||
try {
|
|
||||||
f->open(NOT_OPEN);
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
test_result(status == -E_INVAL, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If a file doesn't exist, it can't be opened for reading
|
|
||||||
*/
|
|
||||||
void test_c(int testno, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
File *f = get_file(testno, 'c', valid, hint);
|
|
||||||
try {
|
|
||||||
f->open(OPEN_READ);
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
if (valid == false)
|
|
||||||
test_result(status == -E_INVAL, true);
|
|
||||||
else
|
|
||||||
test_result(status == -E_EXIST, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Writing to FILE_TYPE_LEGACY is not supported
|
|
||||||
*/
|
|
||||||
void test_d(int testno, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
File *f = get_file(testno, 'd', valid, hint);
|
|
||||||
|
|
||||||
try {
|
|
||||||
f->open(OPEN_WRITE);
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid == false)
|
|
||||||
test_result(status == -E_INVAL, true);
|
|
||||||
else if (hint == FILE_TYPE_LEGACY)
|
|
||||||
test_result(status == -E_IO, true);
|
|
||||||
else
|
|
||||||
test_result(status == 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempt to close a file that was never opened
|
|
||||||
*/
|
|
||||||
void test_e(int testno, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
File *f = get_file(testno, 'e', valid, hint);
|
|
||||||
try {
|
|
||||||
f->close();
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
test_result(status == 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Open for writing, then for reading
|
|
||||||
*/
|
|
||||||
void test_f(int testno, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
File *f;
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
if (inval_or_legacy(valid, hint))
|
|
||||||
return;
|
|
||||||
|
|
||||||
f = get_file(testno, 'f', valid, hint);
|
|
||||||
|
|
||||||
try {
|
|
||||||
f->open(OPEN_WRITE);
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
test_result(status == 0, false);
|
|
||||||
print(" ");
|
|
||||||
|
|
||||||
status = 0;
|
|
||||||
try {
|
|
||||||
f->open(OPEN_READ);
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
test_result(status == -E_IO, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a file, then open for reading
|
|
||||||
*/
|
|
||||||
void test_g(int testno, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
File *f;
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
if (inval_or_legacy(valid, hint))
|
|
||||||
return;
|
|
||||||
|
|
||||||
f = get_file(testno, 'g', valid, hint);
|
|
||||||
|
|
||||||
try {
|
|
||||||
f->open(OPEN_WRITE);
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
if (!test_result(status == 0, false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
f->close();
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
if (!test_result(status == 0, true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
f->open(OPEN_READ);
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
if (!test_result(status == 0, true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
f->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test doing IO to a file
|
|
||||||
*/
|
|
||||||
void test_h(int testno, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
File *f;
|
|
||||||
int a, b, c, d, e, status = 0;
|
|
||||||
|
|
||||||
if (inval_or_legacy(valid, hint))
|
|
||||||
return;
|
|
||||||
f = get_file(testno, 'h', valid, hint);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Write data to file
|
|
||||||
*/
|
|
||||||
try {
|
|
||||||
f->open(OPEN_WRITE);
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
if (!test_result(status == 0, false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
(*f) << "Hello, World!" << std::endl;
|
|
||||||
(*f) << "This is a multi-line file =)" << std::endl;
|
|
||||||
(*f) << "1 2 3 4 5" << std::endl;
|
|
||||||
|
|
||||||
try {
|
|
||||||
f->close();
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
if (!test_result(status == 0, true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read data back from disk
|
|
||||||
*/
|
|
||||||
try {
|
|
||||||
f->open(OPEN_READ);
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
if (!test_result(status == 0, true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
print(" File version is: %u\n", f->get_version());
|
|
||||||
print(" \"%s\"\n", f->getline().c_str());
|
|
||||||
if (!test_result(f->good(), true))
|
|
||||||
return;
|
|
||||||
print(" \"%s\"\n", f->getline().c_str());
|
|
||||||
if (!test_result(f->good(), true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
(*f) >> a >> b >> c >> d >> e;
|
|
||||||
if (!test_result(f->good(), true))
|
|
||||||
return;
|
|
||||||
print(" a: %d, b: %d, c: %d, d: %d, e: %d\n", a, b, c, d, e);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Close file
|
|
||||||
*/
|
|
||||||
try {
|
|
||||||
f->close();
|
|
||||||
} catch (int error) {
|
|
||||||
status = error;
|
|
||||||
}
|
|
||||||
test_result(status == 0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void(*tests[])(int, bool, FileLocHint) = {
|
|
||||||
test_a,
|
|
||||||
test_b,
|
|
||||||
test_c,
|
|
||||||
test_d,
|
|
||||||
test_e,
|
|
||||||
test_f,
|
|
||||||
test_g,
|
|
||||||
test_h,
|
|
||||||
};
|
|
||||||
static unsigned int num_tests = sizeof(tests) / sizeof(void (*)(int, bool, FileLocHint));
|
|
||||||
|
|
||||||
void run_test(int testno, bool valid, FileLocHint hint)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < num_tests; i++)
|
|
||||||
tests[i](testno, valid, hint);
|
|
||||||
print("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
run_test(1, true, FILE_TYPE_CONFIG);
|
|
||||||
run_test(2, false, FILE_TYPE_CONFIG);
|
|
||||||
run_test(3, true, FILE_TYPE_DATA);
|
|
||||||
run_test(4, false, FILE_TYPE_DATA);
|
|
||||||
run_test(5, true, FILE_TYPE_LEGACY);
|
|
||||||
run_test(6, false, FILE_TYPE_LEGACY);
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
Test 1a: File path is /home/anna/.config/ocarina-test/file.1a
|
|
||||||
Test 1b: ERROR: NOT_OPEN is not a legal OpenMode (/home/anna/.config/ocarina-test/file.1b)
|
|
||||||
Passed
|
|
||||||
Test 1c: ERROR: File does not exist (/home/anna/.config/ocarina-test/file.1c)
|
|
||||||
Passed
|
|
||||||
Test 1d: Passed
|
|
||||||
Test 1e: Passed
|
|
||||||
Test 1f: Passed
|
|
||||||
ERROR: File is already open (/home/anna/.config/ocarina-test/file.1f)
|
|
||||||
Passed
|
|
||||||
Test 1g: Passed
|
|
||||||
Passed
|
|
||||||
Passed
|
|
||||||
Test 1h: Passed
|
|
||||||
Passed
|
|
||||||
Passed
|
|
||||||
File version is: 0
|
|
||||||
"Hello, World!"
|
|
||||||
Passed
|
|
||||||
"This is a multi-line file =)"
|
|
||||||
Passed
|
|
||||||
Passed
|
|
||||||
a: 1, b: 2, c: 3, d: 4, e: 5
|
|
||||||
Passed
|
|
||||||
|
|
||||||
Test 2a: File path is INVALID
|
|
||||||
Test 2b: ERROR: NOT_OPEN is not a legal OpenMode (INVALID)
|
|
||||||
Passed
|
|
||||||
Test 2c: ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened
|
|
||||||
Passed
|
|
||||||
Test 2d: ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened
|
|
||||||
Passed
|
|
||||||
Test 2e: Passed
|
|
||||||
|
|
||||||
Test 3a: File path is /home/anna/.local/share/ocarina-test/file.3a
|
|
||||||
Test 3b: ERROR: NOT_OPEN is not a legal OpenMode (/home/anna/.local/share/ocarina-test/file.3b)
|
|
||||||
Passed
|
|
||||||
Test 3c: ERROR: File does not exist (/home/anna/.local/share/ocarina-test/file.3c)
|
|
||||||
Passed
|
|
||||||
Test 3d: Passed
|
|
||||||
Test 3e: Passed
|
|
||||||
Test 3f: Passed
|
|
||||||
ERROR: File is already open (/home/anna/.local/share/ocarina-test/file.3f)
|
|
||||||
Passed
|
|
||||||
Test 3g: Passed
|
|
||||||
Passed
|
|
||||||
Passed
|
|
||||||
Test 3h: Passed
|
|
||||||
Passed
|
|
||||||
Passed
|
|
||||||
File version is: 0
|
|
||||||
"Hello, World!"
|
|
||||||
Passed
|
|
||||||
"This is a multi-line file =)"
|
|
||||||
Passed
|
|
||||||
Passed
|
|
||||||
a: 1, b: 2, c: 3, d: 4, e: 5
|
|
||||||
Passed
|
|
||||||
|
|
||||||
Test 4a: File path is INVALID
|
|
||||||
Test 4b: ERROR: NOT_OPEN is not a legal OpenMode (INVALID)
|
|
||||||
Passed
|
|
||||||
Test 4c: ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened
|
|
||||||
Passed
|
|
||||||
Test 4d: ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened
|
|
||||||
Passed
|
|
||||||
Test 4e: Passed
|
|
||||||
|
|
||||||
Test 5a: File path is /home/anna/.ocarina-test/library/file.5a
|
|
||||||
Test 5b: ERROR: NOT_OPEN is not a legal OpenMode (/home/anna/.ocarina-test/library/file.5b)
|
|
||||||
Passed
|
|
||||||
Test 5c: ERROR: File does not exist (/home/anna/.ocarina-test/library/file.5c)
|
|
||||||
Passed
|
|
||||||
Test 5d: ERROR: Cannot write to legacy files (/home/anna/.ocarina-test/library/file.5d)
|
|
||||||
Passed
|
|
||||||
Test 5e: Passed
|
|
||||||
|
|
||||||
Test 6a: File path is INVALID
|
|
||||||
Test 6b: ERROR: NOT_OPEN is not a legal OpenMode (INVALID)
|
|
||||||
Passed
|
|
||||||
Test 6c: ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened
|
|
||||||
Passed
|
|
||||||
Test 6d: ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened
|
|
||||||
Passed
|
|
||||||
Test 6e: Passed
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
Import("env")
|
Import("env", "lib")
|
||||||
|
|
||||||
def compile_utility(name):
|
build = []
|
||||||
return env.Program(name, "%s.cpp" % name)
|
for f in Glob("*.cpp"):
|
||||||
|
src = str(f)
|
||||||
|
name = src.split(".")[0]
|
||||||
|
build += [ env.Program(name, [src] + lib) ]
|
||||||
|
|
||||||
build = compile_utility("version")
|
|
||||||
Return("build")
|
Return("build")
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 (c) Anna Schumaker.
|
||||||
|
* Do stuff with files
|
||||||
|
*
|
||||||
|
* Usage: files -D|-L [-c -g -o {R, W, N} -O -r -v -w] name [DATA]
|
||||||
|
*
|
||||||
|
* -D: FILE_TYPE_DATA
|
||||||
|
* -L: FILE_TYPE_LEGACY
|
||||||
|
*
|
||||||
|
* -c: Test closing the file
|
||||||
|
* -g: Read file using getline()
|
||||||
|
* -o: Open the file for READ, WRITE, or NOT_OPEN
|
||||||
|
* -O: Open the file a second time
|
||||||
|
* -r: Read data from file
|
||||||
|
* -v: Print version and exit
|
||||||
|
* -w: Write data to file
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <file.h>
|
||||||
|
#include <print.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
enum action_t { CLOSE, OPEN, PATHS, READ, VERSION, WRITE };
|
||||||
|
|
||||||
|
int print_version(File &f)
|
||||||
|
{
|
||||||
|
print("%u\n", f.get_version());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_filepath(File &f)
|
||||||
|
{
|
||||||
|
print("%s\n", f.get_filepath());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int open_file(File &f, OpenMode mode)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
f.open(mode);
|
||||||
|
return 0;
|
||||||
|
} catch (int err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int c, ret;
|
||||||
|
action_t action = PATHS;
|
||||||
|
FileLocHint hint = FILE_TYPE_INVALID;
|
||||||
|
OpenMode mode = NOT_OPEN;
|
||||||
|
bool second_open = false;
|
||||||
|
bool getline = false;
|
||||||
|
std::string file;
|
||||||
|
std::string data;
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "cDgLo:Orvw")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
action = CLOSE;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
hint = FILE_TYPE_DATA;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
getline = true;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
hint = FILE_TYPE_LEGACY;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
action = OPEN;
|
||||||
|
switch (optarg[0]) {
|
||||||
|
case 'R':
|
||||||
|
mode = OPEN_READ;
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
mode = OPEN_WRITE;
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
mode = NOT_OPEN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print("Invalid open mode\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
second_open = true;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
action = READ;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
action = VERSION;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
action = WRITE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc)
|
||||||
|
file = argv[optind++];
|
||||||
|
if (optind < argc)
|
||||||
|
data = argv[optind++];
|
||||||
|
|
||||||
|
File f(file, hint);
|
||||||
|
switch (action) {
|
||||||
|
case CLOSE:
|
||||||
|
ret = open_file(f, OPEN_WRITE);
|
||||||
|
if (ret == 0) {
|
||||||
|
f.close();
|
||||||
|
ret = open_file(f, OPEN_WRITE);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
case OPEN:
|
||||||
|
ret = open_file(f, mode);
|
||||||
|
if ((ret == 0) && (second_open == true))
|
||||||
|
ret = open_file(f, mode);
|
||||||
|
return ret;
|
||||||
|
case PATHS:
|
||||||
|
return print_filepath(f);
|
||||||
|
case READ:
|
||||||
|
ret = open_file(f, OPEN_READ);
|
||||||
|
if (ret == 0) {
|
||||||
|
do {
|
||||||
|
if (getline == true)
|
||||||
|
data = f.getline();
|
||||||
|
else
|
||||||
|
f >> data;
|
||||||
|
if (f.good() == false)
|
||||||
|
break;
|
||||||
|
print("%s\n", data.c_str());
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
case VERSION:
|
||||||
|
return print_version(f);
|
||||||
|
case WRITE:
|
||||||
|
ret = open_file(f, OPEN_WRITE);
|
||||||
|
if (ret == 0)
|
||||||
|
f << data << std::endl;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Loading…
Reference in New Issue