Skip to content

Commit

Permalink
test: add unit tests for core functionality
Browse files Browse the repository at this point in the history
* refactor Symlink to accept strings on init
* docs: improve
* ci: use linter
  • Loading branch information
kucaahbe committed Jan 20, 2024
1 parent 65cd9ef commit 65c6dd7
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 45 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
with:
compiler: ${{ matrix.dc }}

- name: 'dub build && dub test'
- name: 'dub build/lint/test'
run: |
dub_configuration=application
Expand All @@ -63,6 +63,7 @@ jobs:
# fi
dub build --compiler=$DC --build=release-debug --config=$dub_configuration # --verbose
dub lint --compiler=$DC --config=$dub_configuration # --verbose
dub test --compiler=$DC --config=$dub_configuration # --verbose
- name: 'Upload binary'
Expand Down
8 changes: 1 addition & 7 deletions source/cli.d
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,7 @@ void cli_install(ref Config app_config)
output ~= " -> ";
output ~= symlink.source.absolute;
} else {
string backup;
if (symlink.destination.exists) {
backup = symlink.backup();
symlink.link();
} else {
symlink.link();
}
string backup = symlink.link();
output ~= " -> ";
output ~= symlink.source.absolute;
if (backup)
Expand Down
4 changes: 2 additions & 2 deletions source/config_parser.d
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ private void processSDLNode(ref SDLNode node, ref Config config)
auto values = child_node.values;
if (values.length == 2 && values.all!"a.isText") {
config.symlinks ~= [Symlink(
Path(values[0].textValue),
Path(values[1].textValue)
values[0].textValue,
values[1].textValue
)];
} else {
// TODO: replace TODO with ln content: "ln "val1""
Expand Down
112 changes: 101 additions & 11 deletions source/path.d
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,124 @@ struct Path
{
/** original (may be relative) path */
string orig;
/** abosulute path of the `orig` */
/** an _absolute path of the [orig] */
string absolute;

/** creates Path
/** creates [Path]
* Params:
* path = `orig`
* path = [orig]
*/
this(string path)
{
orig = path;
absolute = orig.expandTilde.absolutePath().asNormalizedPath().array;
}

/** Returns: true if Path exists in a file system */
bool exists()
/// _path processing
unittest
{
import std.file;
import std.process;

environment["HOME"] = "/my/home";
immutable auto cwd = getcwd();
immutable auto pathAbsolute = cwd ~ '/' ~ "file";

// expands tilde
auto path = Path("~/file");
assert(path.absolute == "/my/home/file", path.absolute ~ "!=" ~ "/my/home/file");

// absolute path
path = Path("file");
assert(path.absolute == pathAbsolute, path.absolute ~ "!=" ~ pathAbsolute);

// normalise path
path = Path("./file");
assert(path.absolute == pathAbsolute, path.absolute ~ "!=" ~ pathAbsolute);
}

/** Returns: true if [Path] _exists in a file system */
bool exists() inout
{
return absolute.exists;
}

/** Returns: true if Path is a directory in a file system */
bool isDir()
/// returns true if [absolute] _exists
unittest
{
return absolute.isDir;
import std.file;
import std.process;
import test_file;

if (".test".exists) ".test".rmdirRecurse;
scope(exit) if (".test".exists) ".test".rmdirRecurse;

immutable auto file_path = ".test/app/source";

const auto path = Path(file_path);
assert(!path.exists);

TestFile(file_path, "content").create;

assert(path.exists);
}

/** Returns: true if [Path] is a directory in a file system */
bool isDir() inout
{
return exists && absolute.isDir;
}

/** Returns: true if Path is a symbolic link in a file system */
bool isSymlink()
/// returns true if [absolute] exists and is a directory
unittest
{
return absolute.isSymlink;
import std.file;
import std.process;
import test_file;

if (".test".exists) ".test".rmdirRecurse;
scope(exit) if (".test".exists) ".test".rmdirRecurse;

immutable auto dir_path = ".test/app";

const auto path = Path(dir_path);
assert(!path.isDir);

dir_path.mkdirRecurse;

assert(path.isDir);
}

/** Returns: true if [Path] is a symbolic link in a file system */
bool isSymlink() inout
{
return exists && absolute.isSymlink;
}

/// returns true if [absolute] exists and is a symbolic link
unittest
{
import std.file;
import std.process;
import test_file;

if (".test".exists) ".test".rmdirRecurse;
scope(exit) if (".test".exists) ".test".rmdirRecurse;

immutable auto dir_path = ".test/app";

const auto path = Path(dir_path);
assert(!path.isSymlink);

dir_path.mkdirRecurse;
assert(!path.isSymlink);

TestFile(dir_path~'/'~"file", "content");
assert(!path.isSymlink);

remove(path.absolute);
symlink("other_file_actually_doesnt_exist_but_who_cares", path.absolute);

assert(path.isSymlink);
}
}
185 changes: 161 additions & 24 deletions source/symlink.d
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,42 @@ import path;

/** symbolic link */
struct Symlink {
/** source path */
/** _source file [Path] */
Path source;
/** destination path */
/** _destination file [Path] */
Path destination;

/** creates new [Symlink]
* Params:
* src = source path (may be relative to current working directory)
* dest = destination path
*/
this(string src, string dest)
{
source = Path(src);
destination = Path(dest);
}

/// initialisation could be done with strings
unittest
{
import std.file;

immutable auto srcPath = ".test/app/source";
immutable auto destPath = ".test/home/destination";
immutable auto cwd = getcwd();

const auto link = Symlink(srcPath, destPath);

immutable auto sourcePathAbsolute = cwd ~ '/' ~ srcPath;
immutable auto destinationPathAbsolute = cwd ~ '/' ~ destPath;

assert(link.source.absolute == sourcePathAbsolute,
link.source.absolute ~ "!=" ~ sourcePathAbsolute);
assert(link.destination.absolute == destinationPathAbsolute,
link.destination.absolute ~ "!=" ~ destinationPathAbsolute);
}

/** test symlink correctness
* Returns: source == destination
*/
Expand All @@ -19,40 +50,130 @@ struct Symlink {
return source.absolute == destinationActual;
}

/** creates symbolic link in the file system */
void link()
/// returns true if symlink exists and false otherwise
unittest
{
symlink(source.absolute, destination.absolute);
import std.file;
import test_file;

if (".test".exists) ".test".rmdirRecurse;
scope(exit) if (".test".exists) ".test".rmdirRecurse;

auto srcPath = ".test/app/source";
auto destPath = ".test/home/destination";
TestFile(srcPath, "content").create;
TestFile(destPath).create;

auto link = Symlink(srcPath, destPath);

assert(!link.ok);
link.link();
assert(link.ok);
}

/** creates backup of the destination
* Returns: pach of the backup file if backup was created
* Bugs: the name of the backup file is currently incorrect (must be
/** creates symbolic _link in the file system
* (creates backup of the destination if it exists)
* Returns: path of the backup file if backup was created
* Bugs: the name of the backup file is currently incorrect (must be
* `filename.TIMESTAMP.bak`)
*/
string backup()
string link()
{
auto backup = destination.absolute ~ ".bak";
try
{
rename(destination.absolute, backup);
} catch (FileException e)
{
import std.stdio: writeln;
writeln("DEBUG:", destination.absolute, " ", backup);
writeln(e.message, " ", e.errno);
backup = "FAILED BACKUP";
}
return backup;
string backupPath;

if (destination.exists)
backupPath = backup();

symlink(source.absolute, destination.absolute);

return backupPath;
}

/** Returns: actual `Path` of the desctination */
Path actual()
/// **success case 1**: creates symlink when destination doesn't exist
unittest
{
import std.file;
import std.path;

if (".test".exists) ".test".rmdirRecurse;
scope(exit) if (".test".exists) ".test".rmdirRecurse;

auto srcPath = ".test/app/source";
auto destPath = ".test/home/destination";

srcPath.dirName.mkdirRecurse;
destPath.dirName.mkdirRecurse;

auto link = Symlink(srcPath, destPath);
link.link();

assert(link.destination.absolute.isSymlink);
assert(link.destination.absolute.readLink == link.source.absolute);
}

/** **success case 2**: creates symlink when destination exists (creating backup
* and returning it's path)
*/
unittest
{
import std.file;
import std.path;
import test_file;

if (".test".exists) ".test".rmdirRecurse;
scope(exit) if (".test".exists) ".test".rmdirRecurse;

auto srcPath = ".test/app/source";
auto destPath = ".test/home/destination";
immutable auto cwd = getcwd();

TestFile(srcPath, "src content").create;
TestFile(destPath, "dest content").create;

auto link = Symlink(srcPath, destPath);
immutable auto backup = link.link();

immutable auto destPathBackup = cwd ~ '/' ~ destPath ~ ".bak";

assert(link.destination.absolute.isSymlink);
assert(link.destination.absolute.readLink == link.source.absolute);
assert(backup == destPathBackup, backup ~ "!=" ~ destPathBackup);
}

/** Returns: _actual [Path] of the destination */
Path actual() const
{
return Path(destinationActual);
}

private string destinationActual()
/// actual path of the symbolic link destination
// TODO: cases to test:
// * destination doesn't exists
// * destination is not a symlink - this is what's done
// * destination is a symlink
unittest
{
import std.file;
import std.path;
import test_file;

if (".test".exists) ".test".rmdirRecurse;
scope(exit) if (".test".exists) ".test".rmdirRecurse;

auto srcPath = ".test/app/source";
auto destPath = ".test/home/destination";
immutable auto cwd = getcwd();

TestFile(srcPath, "src content").create;
TestFile(destPath, "dest content").create;

const auto link = Symlink(srcPath, destPath);
immutable auto actualPathAbsolute = cwd ~ '/' ~ destPath;

assert(link.actual.absolute == actualPathAbsolute);
}

private string destinationActual() const
{
if (!destination.absolute.exists) {
return [];
Expand All @@ -62,4 +183,20 @@ struct Symlink {
return destination.absolute;
}
}

private string backup()
{
auto backup = destination.absolute ~ ".bak";
try
{
rename(destination.absolute, backup);
} catch (FileException e)
{
import std.stdio: writeln;
writeln("DEBUG:", destination.absolute, " ", backup);
writeln(e.message, " ", e.errno);
backup = "FAILED BACKUP";
}
return backup;
}
}
Loading

0 comments on commit 65c6dd7

Please sign in to comment.