Compare commits
37 Commits
82351fb5d9
...
main
Author | SHA1 | Date | |
---|---|---|---|
0255031150
|
|||
55388fea39
|
|||
27f6db5410
|
|||
1458620bee
|
|||
2dbe0b08c4
|
|||
d95aa517df
|
|||
66a7c0e3e7
|
|||
69951be543
|
|||
b483c72df7
|
|||
e00766cce8
|
|||
76b490bc83
|
|||
d77349ce50
|
|||
741f349028
|
|||
c3acf30c66
|
|||
1580865b56
|
|||
d162919b3d
|
|||
90b0c98675
|
|||
41362eccf1
|
|||
f02133d6ab
|
|||
f43c1d433a
|
|||
d7f4a9c299
|
|||
473d88b3c9
|
|||
c86f125ed3
|
|||
ca5146e17c
|
|||
ec5cb5bb17
|
|||
907f9b742d
|
|||
d743baa4d4
|
|||
70ea680b0f
|
|||
4761ef5040
|
|||
f8b75eb91b
|
|||
f7102e1fc4
|
|||
90f436fb23
|
|||
dcbc0b23d6
|
|||
f22c4ce044
|
|||
353b86af65
|
|||
1f6761bf5a
|
|||
a847ff6301
|
26
database.sql
26
database.sql
@ -1,26 +1,32 @@
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE IF NOT EXISTS "tags" (
|
||||
"id" INTEGER,
|
||||
"label" text,
|
||||
"label" TEXT,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "files" (
|
||||
"id" INTEGER,
|
||||
"filename" text,
|
||||
"filename" TEXT NOT NULL,
|
||||
"path" TEXT,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "tags_ties" (
|
||||
"id" INTEGER,
|
||||
"tid" INTEGER,
|
||||
"fid" INTEGER,
|
||||
PRIMARY KEY("id" AUTOINCREMENT),
|
||||
FOREIGN KEY("tid") REFERENCES "tags"("id"),
|
||||
CREATE TABLE IF NOT EXISTS "metadata" (
|
||||
"fid" INTEGER NOT NULL,
|
||||
"name" TEXT,
|
||||
PRIMARY KEY("fid"),
|
||||
FOREIGN KEY("fid") REFERENCES "files"("id")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "tags_ties" (
|
||||
"fid" INTEGER NOT NULL,
|
||||
"tid" INTEGER NOT NULL,
|
||||
PRIMARY KEY("fid", "tid"),
|
||||
FOREIGN KEY("fid") REFERENCES "files"("id"),
|
||||
FOREIGN KEY("tid") REFERENCES "tags"("id")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "hashes" (
|
||||
"id" INTEGER,
|
||||
"md5" text,
|
||||
"fid" int,
|
||||
"md5" TEXT NOT NULL,
|
||||
"fid" INTEGER NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT),
|
||||
FOREIGN KEY("fid") REFERENCES "files"("id")
|
||||
);
|
||||
|
245
tag.sh
245
tag.sh
@ -1,24 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ueo pipefail
|
||||
# usage:
|
||||
# - init - create an empty database
|
||||
# - import - import $FILE or import $DIR, both can be multiple values
|
||||
# - autoimport ($REGEX|video) $FOLDER - will import all files in $FOLDER with a $REGEX filter,
|
||||
# or one of the preset filters:
|
||||
# - video
|
||||
# - add (tag|filename) $VALUE - adds a new tag or filename of $VALUE
|
||||
# - list (tags|files) - lists all the values in a table
|
||||
# - bytag $TAG - list all files by tag label
|
||||
# - tag $FILENAME $TAG - tag $FILENAME with $TAG, $TAG can be repeated
|
||||
# - listtags $FILENAME - list all tags for $FILENAME
|
||||
[ "${DEBUG:-0}" = "1" ] && set -x
|
||||
: <<'DOCS'
|
||||
=head1 NAME
|
||||
|
||||
DB_FILE="tags.db"
|
||||
DB_SCHEMA="database.sql"
|
||||
tags is a tool for keeping file tags in a SQLite database.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
tag [OPTIONS] <init|import|add|list|bytag|listtags>
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<init>
|
||||
|
||||
Create an empty database.
|
||||
|
||||
=item B<import> I<filename>
|
||||
|
||||
Import filename, can be single value or multiple values.
|
||||
|
||||
=item B<autoimport> I<regexp> I<path>
|
||||
|
||||
Import all files in I<path>. Can be filtered using custom I<regexp>, or one of the regular expression presets (currently only "video").
|
||||
|
||||
=item B<add> I<type> I<value>
|
||||
|
||||
Adds a new item of the specified I<type>: C<tag>, C<file>, C<hash>.
|
||||
|
||||
=item B<list> I<type> I<[--bytag|--byfile]>
|
||||
|
||||
List C<tags>, C<files>, C<hashes> (these are all table names, internally).
|
||||
|
||||
=item B<tag> I<[-i|--interactive]> I<--file|--id> I<tag>
|
||||
|
||||
Tags I<filename> with one or more I<tag>.
|
||||
|
||||
=back
|
||||
|
||||
=head1 TODO
|
||||
|
||||
* adjust sqlite_insert_multiple to allow defining columns per insert, currently it's hardcoded to 2
|
||||
* merge sqlite_insert_single and sqlite_insert_multiple probably as result of the above
|
||||
* listtags doesn't work with full path
|
||||
* listtags doesn't work after tagging a file
|
||||
|
||||
=head1 LICENSE AND COPYRIGHT
|
||||
|
||||
=cut
|
||||
DOCS
|
||||
|
||||
SCRIPT_DIR=$(dirname "$(readlink "$0")")
|
||||
SCRIPT_NAME=$(basename "$0")
|
||||
|
||||
|
||||
DB_FILE="${PWD}/tags.db"
|
||||
[ "${1:-}" = "--db" ] && DB_FILE=$(readlink -f "${2/#~/$HOME}") && shift 2
|
||||
|
||||
|
||||
DB_SCHEMA="${SCRIPT_DIR}/database.sql"
|
||||
declare -A FILTERS
|
||||
FILTERS[video]="avi|flv|mkv|mov|mp4|mpg|ogv|webm|wmv"
|
||||
# LIST_RECURSIVE_FILES_CMD="find ${FOLDER} -type f -regextype posix-extended -iregex \"${REGEX}\""
|
||||
|
||||
[ "${DEBUG:-0}" = "1" ] && set -x
|
||||
|
||||
fail() {
|
||||
echo "$1" >&2;
|
||||
@ -30,9 +75,10 @@ log() {
|
||||
}
|
||||
|
||||
init() {
|
||||
[ -f "$DB_FILE" ] && fail "Database file \"$DB_FILE\" already exists. Aborting."
|
||||
cat "$DB_SCHEMA" | sqlite3 "$DB_FILE"
|
||||
log "Empty database file \"$DB_FILE\" created."
|
||||
# DB_NAME
|
||||
[ -f "$1" ] && fail "Database file \"$1\" already exists. Aborting."
|
||||
sqlite3 "$1" < "$DB_SCHEMA"
|
||||
log "Empty database file \"$1\" created."
|
||||
}
|
||||
|
||||
enumerate_positional_parameters() {
|
||||
@ -49,16 +95,18 @@ listtags() {
|
||||
[ -z "$1" ] && fail "No filename supplied."
|
||||
FILENAME="$1"
|
||||
shift
|
||||
LIMIT="${1:-0}"
|
||||
|
||||
# ! file_exists_in_db "$FILENAME" && fail "File '$FILENAME' does not exist in database."
|
||||
|
||||
LIMIT="${1:-0}"
|
||||
ADDITIONAL_QUERY=""
|
||||
[ $LIMIT -gt 0 ] && ADDITIONAL_QUERY="LIMIT $LIMIT"
|
||||
|
||||
sqlite_query \
|
||||
"SELECT filename, label from files \
|
||||
INNER JOIN tags_ties ON tags_ties.id = files.id \
|
||||
INNER JOIN tags ON tags.id = tags_ties.id \
|
||||
WHERE filename = \"$FILENAME\"\
|
||||
INNER JOIN tags_ties ON tags_ties.fid = files.id \
|
||||
INNER JOIN tags ON tags.id = tags_ties.tid \
|
||||
WHERE filename LIKE \"%$FILENAME%\"\
|
||||
$ADDITIONAL_QUERY"
|
||||
}
|
||||
|
||||
@ -66,13 +114,16 @@ add() {
|
||||
# $TYPE(tag|path|hash) $VALUE1..$VALUEN
|
||||
[ "$1" = "tag" ] && shift && add_tag "$@" && exit 0
|
||||
[ "$1" = "path" ] && shift && add_path "$@" && exit 0
|
||||
[ "$1" = "hash" ] && fail "Adding hashes is not implemented yet." && TABLE="hashes" && COLUMN="md5,filename"
|
||||
exit 0
|
||||
[ "$1" = "hash" ] && shift && add_hash "$@" && exit 0
|
||||
[ "$1" = "file" ] && fail "Use \"$SCRIPT_NAME add path\" instead."
|
||||
pod2usage "$0"
|
||||
exit 1
|
||||
}
|
||||
|
||||
sqlite_query() {
|
||||
# $QUERY
|
||||
sqlite3 -table "$DB_FILE" "$1"
|
||||
dbfile_exists || fail "Database file '$DB_FILE' does not exist. Use '$SCRIPT_NAME init' to create default one, or specify filename with '--db'."
|
||||
sqlite3 "$DB_FILE" "$1"
|
||||
}
|
||||
|
||||
sqlite_lastrows() {
|
||||
@ -80,7 +131,7 @@ sqlite_lastrows() {
|
||||
sqlite_query "SELECT * FROM \"$1\" ORDER BY id DESC LIMIT ${2}"
|
||||
}
|
||||
|
||||
sqlite_insert() {
|
||||
sqlite_insert_single() {
|
||||
# $TABLE $COLUMN $VALUES
|
||||
# $VALUES can be comma-delimited
|
||||
[ -z "$1" ] && fail "No table specified."
|
||||
@ -97,7 +148,29 @@ sqlite_insert() {
|
||||
VALUES+=",(\"$1\")"
|
||||
done
|
||||
fi
|
||||
QUERY="INSERT INTO $TABLE ($COLUMN) VALUES ${VALUES} RETURNING *;"
|
||||
QUERY="PRAGMA foreign_keys=ON;"
|
||||
QUERY+="INSERT INTO $TABLE ($COLUMN) VALUES ${VALUES} RETURNING *;"
|
||||
sqlite_query "$QUERY"
|
||||
}
|
||||
|
||||
sqlite_insert_multi() {
|
||||
# $TABLE $COLUMN $VALUE1 $VALUE2
|
||||
[ -z "${1:-}" ] && fail "No table specified."
|
||||
TABLE="$1"
|
||||
[ -z "${2:-}" ] && { pod2usage "$0"; exit 1 ; }
|
||||
COLUMN="$2"
|
||||
[[ -z "${3:-}" || -z "${4:-}" ]] && { pod2usage "$0"; exit 1 ; }
|
||||
shift 2
|
||||
VALUES="($1,$2)"
|
||||
if [[ ! -z "${3:-}" && ! -z "${4:-}" ]]; then
|
||||
while true; do
|
||||
shift 2
|
||||
[[ -z "${1:-}" && -z "${2:-}" ]] && break
|
||||
VALUES+=",($1,$2)"
|
||||
done
|
||||
fi
|
||||
QUERY="PRAGMA foreign_keys=ON;"
|
||||
QUERY+="INSERT INTO $TABLE ($COLUMN) VALUES ${VALUES} RETURNING *;"
|
||||
sqlite_query "$QUERY"
|
||||
}
|
||||
|
||||
@ -105,27 +178,90 @@ add_tag() {
|
||||
# $TAG1...$TAGN
|
||||
TABLE="tags"
|
||||
COLUMN="label"
|
||||
sqlite_insert "$TABLE" "$COLUMN" "$@"
|
||||
sqlite_insert_single "$TABLE" "$COLUMN" "$@"
|
||||
}
|
||||
|
||||
add_path() {
|
||||
# $PATH1..$PATHN
|
||||
TABLE="files"
|
||||
COLUMN="filename"
|
||||
sqlite_insert "$TABLE" "$COLUMN" "$@"
|
||||
COLUMN="filename,path"
|
||||
FILES=()
|
||||
for FILE in "$@"; do
|
||||
[ ! -f "$FILE" ] && fail "File '$FILE' does not exist in the specified path."
|
||||
local FILENAME=$(basename "$FILE")
|
||||
local FILEPATH=$(readlink -f "$(dirname "$FILE")")
|
||||
file_exists_in_db "$FILE" && fail "File '$FILE' already exists in database."
|
||||
FILES+=("\"$FILENAME\"")
|
||||
FILES+=("\"$FILEPATH\"")
|
||||
done
|
||||
sqlite_insert_multi "$TABLE" "$COLUMN" "${FILES[@]}"
|
||||
}
|
||||
|
||||
add_hash() {
|
||||
# $FILE $HASH
|
||||
local TABLE="hashes"
|
||||
local COLUMN="fid,md5"
|
||||
local FILENAME="${1:-}"
|
||||
[ -z "$FILENAME" ] && fail "No file specified."
|
||||
local FID=$(id_by_filename "$FILENAME")
|
||||
[[ "$FID" -eq 0 ]] && fail "File \"$FILENAME\" does not exist in database."
|
||||
sqlite_insert_multi "$TABLE" "$COLUMN" $FID "'$2'"
|
||||
}
|
||||
|
||||
add_path_auto() {
|
||||
# $REGEX $FOLDER
|
||||
find "${2}" -type f -regextype posix-extended -iregex "$1" -exec "$0" import "{}" +
|
||||
find "${2}" -type f -regextype posix-extended -iregex "$1" -exec "$0" --db "$DB_FILE" import "{}" +
|
||||
}
|
||||
|
||||
tag_exists_in_db() {
|
||||
# $TAGLABEL
|
||||
TAG=${1:-}
|
||||
RESULT=$(sqlite_query "SELECT id FROM tags WHERE label = \"$TAG\"")
|
||||
if [[ -z "$RESULT" ]]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
file_exists_in_db() {
|
||||
# $FILENAME
|
||||
local RESULT=$(id_by_filename "$1")
|
||||
if [[ "$RESULT" -eq 0 ]]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
id_by_filename() {
|
||||
# FILENAME
|
||||
local FILENAME=$(basename "${1:-}")
|
||||
local FILEPATH=$(readlink -f "$(dirname "${1:-}")")
|
||||
local RESULT=0
|
||||
RESULT=$(sqlite_query "SELECT id FROM files WHERE filename = \"$FILENAME\" AND path = \"$FILEPATH\"")
|
||||
echo $RESULT
|
||||
}
|
||||
|
||||
dbfile_exists() {
|
||||
if [[ ! -f "$DB_FILE" ]]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
|
||||
[ -z "${1:-}" ] && { pod2usage "$0"; exit 1; }
|
||||
|
||||
if [[ "$1" = "init" ]]; then
|
||||
init
|
||||
init "${2:-$DB_FILE}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ ! -f "$DB_FILE" ] && fail "Database file \"$DB_FILE\" does not exist."
|
||||
|
||||
if [[ "$1" = "import" ]]; then
|
||||
shift
|
||||
add "path" "$@"
|
||||
@ -146,30 +282,39 @@ main() {
|
||||
fi
|
||||
|
||||
if [[ "$1" = "list" ]]; then
|
||||
[ -z "$2" ] && fail "No table supplied."
|
||||
[ -z "${2:-}" ] && { pod2usage "$0"; exit 1 ; }
|
||||
TABLE_NAME="$2"
|
||||
sqlite3 -table "$DB_FILE" "SELECT * FROM \"${TABLE_NAME}\""
|
||||
sqlite_query "SELECT * FROM \"${TABLE_NAME}\""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" = "bytag" ]]; then
|
||||
[ -z "$2" ] && fail "No tag supplied."
|
||||
TAG_NAME="$2"
|
||||
sqlite3 -table "$DB_FILE" "SELECT filename FROM files WHERE id = (SELECT id FROM tags_ties WHERE id = (SELECT id FROM tags WHERE label = \"${TAG_NAME}\"))"
|
||||
sqlite_query "SELECT filename FROM files WHERE id = (SELECT id FROM tags_ties WHERE id = (SELECT id FROM tags WHERE label = \"${TAG_NAME}\"))"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" = "tag" ]]; then
|
||||
shift
|
||||
[ -z "$1" ] && fail "No filename supplied."
|
||||
if [[ "${1:-}" = "-i" || "${1:-}" = "--interactive" ]]; then
|
||||
"$0" --db "$DB_FILE" tagfid \
|
||||
$(sqlite_query "SELECT id,filename,path FROM files" | fzf | xargs -I{} echo '{}' | awk -F'|' '{print $1}') \
|
||||
$(sqlite_query "SELECT label FROM tags" | fzf --multi | xargs -I{} echo '{}' | awk -F'|' '{print $1}')
|
||||
exit 0
|
||||
fi
|
||||
[ -z "${1:-}" ] && fail "No filename supplied."
|
||||
FILENAME="$1"
|
||||
! file_exists_in_db "$FILENAME" && fail "File '$FILENAME' does not exist in database."
|
||||
shift
|
||||
[ -z "$1" ] && fail "No tag supplied."
|
||||
[ -z "${1:-}" ] && fail "No tag supplied."
|
||||
COUNTER=0
|
||||
while true; do
|
||||
LABEL="$1"
|
||||
! tag_exists_in_db "$LABEL" && fail "Tag '$TAG' does not exist in database."
|
||||
shift
|
||||
sqlite3 -table "$DB_FILE" "INSERT INTO tags_ties (id, id) VALUES ((SELECT id FROM files WHERE filename = \"$FILENAME\"),(SELECT id FROM tags WHERE label = \"$LABEL\"))"
|
||||
local FID=$(id_by_filename "$FILENAME")
|
||||
sqlite_query "INSERT INTO tags_ties (fid, tid) VALUES ($FID,(SELECT id FROM tags WHERE label = \"$LABEL\"))"
|
||||
COUNTER=$((COUNTER++))
|
||||
[ -z "${1:-}" ] && break
|
||||
done
|
||||
@ -177,10 +322,34 @@ main() {
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" = "tagfid" ]]; then
|
||||
shift
|
||||
[ -z "${1:-}" ] && fail "No file ID supplied."
|
||||
local FID="$1"
|
||||
shift
|
||||
[ -z "${1:-}" ] && fail "No tag supplied."
|
||||
COUNTER=0
|
||||
while true; do
|
||||
LABEL="$1"
|
||||
! tag_exists_in_db "$LABEL" && fail "Tag '$TAG' does not exist in database."
|
||||
shift
|
||||
sqlite_query "INSERT INTO tags_ties (fid, tid) VALUES ($FID,(SELECT id FROM tags WHERE label = \"$LABEL\"))"
|
||||
COUNTER=$((COUNTER++))
|
||||
[ -z "${1:-}" ] && break
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" = "listtags" ]]; then
|
||||
[ -z "${2:-}" ] && { pod2usage "$0"; exit 1 ; }
|
||||
listtags "$2"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" = "listbyfile" ]]; then
|
||||
sqlite_query "SELECT id,filename FROM files" |\
|
||||
fzf --delimiter="|" --preview "sqlite3 $DB_FILE 'SELECT label FROM tags WHERE id IN (SELECT tid FROM tags_ties WHERE fid ={2})'"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
73
tests/basic.sh
Executable file
73
tests/basic.sh
Executable file
@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ueo pipefail
|
||||
|
||||
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
|
||||
DB_FILE="test.db"
|
||||
FILENAME="test.mp4"
|
||||
|
||||
oneTimeSetUp() {
|
||||
tag --db "$DB_FILE" init >/dev/null
|
||||
}
|
||||
|
||||
oneTimeTearDown() {
|
||||
# need to check if file exists
|
||||
# because of bug in shunit2 that
|
||||
# runs one-time teardown two times
|
||||
# see https://github.com/kward/shunit2/issues/112
|
||||
[ -f "$DB_FILE" ] && rm "$DB_FILE"
|
||||
[ -f "$FILENAME" ] && rm "$FILENAME"
|
||||
}
|
||||
|
||||
testDbCreated() {
|
||||
assertTrue "[[ -f \"$DB_FILE\" ]]"
|
||||
}
|
||||
|
||||
testTagAdded() {
|
||||
tag --db "$DB_FILE" add tag hello >/dev/null
|
||||
RESULT=$(sqlite3 "$DB_FILE" "SELECT label from tags WHERE label = 'hello'")
|
||||
assertEquals "hello" "$RESULT"
|
||||
}
|
||||
|
||||
testFailAddingNonexistentFile() {
|
||||
tag --db "$DB_FILE" import "$FILENAME" 2>/dev/null
|
||||
assertEquals 1 "$?"
|
||||
}
|
||||
|
||||
testFilenameAdded() {
|
||||
touch "$FILENAME"
|
||||
tag --db "$DB_FILE" add path "$FILENAME" >/dev/null
|
||||
RESULT=$(sqlite3 "$DB_FILE" "SELECT filename from files WHERE filename = '$FILENAME'")
|
||||
assertEquals "$FILENAME" "$RESULT"
|
||||
}
|
||||
|
||||
testHashAdded() {
|
||||
local HASH="fbe2153ce0614d76a378b2e6fe07cc9e"
|
||||
tag --db "$DB_FILE" add hash "$FILENAME" "$HASH" >/dev/null
|
||||
RESULT=$(sqlite3 "$DB_FILE" "SELECT md5 from hashes WHERE md5 = '$HASH'")
|
||||
assertEquals "$HASH" "$RESULT"
|
||||
}
|
||||
|
||||
testFailAddingHashOnNonexistentFile() {
|
||||
local HASH="fbe2153ce0614d76a378b2e6fe07cc9e"
|
||||
tag --db "$DB_FILE" add hash "nonexistentfile" "$HASH" 2>/dev/null
|
||||
assertEquals 1 "$?"
|
||||
}
|
||||
|
||||
testTagFile() {
|
||||
tag --db "$DB_FILE" tag "$FILENAME" hello >/dev/null
|
||||
RESULT=$(sqlite3 "$DB_FILE" "SELECT label FROM tags WHERE id = (SELECT tid from tags_ties WHERE fid = (SELECT id FROM files WHERE filename = '$FILENAME'))")
|
||||
assertEquals "hello" "$RESULT"
|
||||
}
|
||||
|
||||
testListingTags() {
|
||||
tag --db "$DB_FILE" listtags "$FILENAME" | grep -q "^$FILENAME|hello$"
|
||||
assertEquals "$?" 0
|
||||
}
|
||||
|
||||
testAddingMultipleTags() {
|
||||
tag --db "$DB_FILE" add tag more more2 >/dev/null
|
||||
RESULT=$(sqlite3 -list "$DB_FILE" "SELECT COUNT(*) from tags WHERE label IN ('hello', 'more', 'more2')")
|
||||
assertEquals 3 "$RESULT"
|
||||
}
|
||||
|
||||
. ../shunit2/shunit2
|
Reference in New Issue
Block a user