aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xsrc/remembering.in163
-rwxr-xr-xtests/cli-opts.sh14
-rwxr-xr-xtests/ranking.sh109
-rwxr-xr-xtests/signals.sh13
4 files changed, 167 insertions, 132 deletions
diff --git a/src/remembering.in b/src/remembering.in
index 2ce8e9c..907ffe1 100755
--- a/src/remembering.in
+++ b/src/remembering.in
@@ -14,12 +14,25 @@ help() {
cat <<-'EOF'
Options:
- -p PROFILE profile to be used for gathering and storing data
- -c COMMAND command to be run, reading from STDIN, writing to STDOUT
- -h, --help show this help
- -V, --version print the version number
+ -p PROFILE profile to be used for gathering and storing
+ data (default: create one based on $PWD)
+ -h, --help show this message
+ -V, --version print the version number
- See "man remembering" for more information.
+ COMMAND command to be ran, reading from
+ STDIN, writing to STDOUT
+
+
+ Explanation FIXME.
+
+ See "man @NAME@" for more information.
+
+
+ Examples:
+
+ FIXME:
+
+ $ FIXME
EOF
}
@@ -27,10 +40,24 @@ version() {
printf '%s %s %s\n' '@NAME@' '@VERSION@' '@DATE@'
}
-missing() {
- printf 'Missing option: %s\n' "$1"
+
+uuid() {
+ od -xN20 /dev/urandom |
+ head -n1 |
+ awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}'
+}
+
+tmpname() {
+ echo "${TMPDIR:-/tmp}/@NAME@.tmpfile.$(uuid)"
}
+mkstemp() {
+ name="$(tmpname)"
+ touch "$name"
+ echo "$name"
+}
+
+
for flag in "$@"; do
case "$flag" in
--)
@@ -50,15 +77,11 @@ for flag in "$@"; do
esac
done
-COMMANDFLAG=
-PROFILEFLAG=
-while getopts 'hc:p:V' name; do
- case "$name" in
- c)
- COMMANDFLAG="$OPTARG"
- ;;
+PROFILE_NAME="$(pwd | tr '/' '!')"
+while getopts 'p:hV' flag; do
+ case "$flag" in
p)
- PROFILEFLAG="$OPTARG"
+ PROFILE_NAME="$OPTARG"
;;
h)
usage
@@ -75,76 +98,68 @@ while getopts 'hc:p:V' name; do
;;
esac
done
+shift $((OPTIND - 1))
-if [ -z "$COMMANDFLAG" ]; then
- missing '-c COMMAND' >&2
- usage >&2
- exit 2
-fi
-if [ -z "$PROFILEFLAG" ]; then
- missing '-p PROFILE' >&2
+if [ $# = 0 ]; then
+ printf 'Missing "-- COMMAND"\n' >&2
usage >&2
exit 2
fi
-COMMAND="$COMMANDFLAG"
-PROFILE="${XDG_DATA_HOME:-$HOME/.local/share}/remembering/$PROFILEFLAG"
+NAME='@NAME@'
+PROFILE="${XDG_DATA_HOME:-$HOME/.local/share}"/$NAME/"$PROFILE_NAME"
if [ ! -e "$PROFILE" ]; then
mkdir -p "$(dirname "$PROFILE")"
touch "$PROFILE"
fi
-MERGED="$(mktemp)"
-FILTERED="$(mktemp)"
-SORTED_STDIN="$(mktemp)"
-cat - > "$SORTED_STDIN"
-
-# sed line to reverse lines, replacing GNU/Linux's tac
-# Taken from:
-# https://unix.stackexchange.com/questions/280685/reverse-sequence-of-a-file-with-posix-tools/280686#comment601716_280686
-xargs -I{} printf '0:%s\n' "{}" < "$SORTED_STDIN" | \
- sort -t: -k2,2 -m - "$PROFILE" | \
- sed '1!x;H;1h;$!d;g' | \
- sort -t: -k2,2 -u > "$MERGED"
-
-xargs -I{} printf 'filter_marker:%s\n' "{}" < "$SORTED_STDIN" | \
- cat - "$PROFILE" | \
- sort -t: -k2,2 | \
- awk '{
- split($0, l, ":")
- rank = l[1]
- entry = substr($0, length(rank) + 2)
- if (rank != "filter_marker") {
- prev_rank = rank
- prev_entry = entry
- } else {
- if (prev_entry == entry) {
- print prev_rank ":" entry
- } else {
- print "0:" entry
- }
+NEXT_PROFILE="$PROFILE".tmp
+MERGED="$(mkstemp)"
+FILTERED="$(mkstemp)"
+trap 'rm -f "$NEXT_PROFILE" "$MERGED" "$FILTERED"' EXIT
+CHOICE="$(
+ cat - |
+ sed 's/^/0 stdin /' |
+ sort -k3 -k1nr - "$PROFILE" |
+ tee "$MERGED" |
+ awk '
+ { rest = substr($0, 3 + length($1) + length($2)) }
+ $2 == "profile" { seen[rest] += $1 }
+ $2 == "stdin" { printf "%s %s\n", seen[rest]+0, rest }
+ ' |
+ sort -k1nr |
+ cut -d' ' -f2- |
+ "$@"
+)"
+
+if [ -z "$CHOICE" ]; then
+ exit
+fi
+
+cat "$MERGED" |
+ cut -d' ' -f1,3- |
+ uniq -f1 |
+ awk -vCHOICE="$CHOICE" '
+ BEGIN { inc = 1 }
+
+ { rest = substr($0, 2 + length($1)) }
+
+ rest == CHOICE {
+ printf "%s profile %s\n", $1 + inc, rest
+ found = 1
+ next
}
- }' > "$FILTERED"
-
-CHOICE="$(sort -t: -k1nr,1 -k2,2 < "$FILTERED" | \
- cut -d: -f2- | \
- sh -c "$COMMAND")"
-
-if [ -n "$CHOICE" ]; then
- NEW_PROFILE="$(mktemp)"
- awk -v choice="$CHOICE" '{
- split($0, l, ":")
- rank = l[1]
- entry = substr($0, length(rank) + 2)
- if (entry == choice) {
- # Naively increment ranking by one
- print rank + 1 ":" entry
- } else {
- print rank ":" entry
+
+ { printf "%s profile %s\n", $1, rest }
+
+ END {
+ if (!found) {
+ printf "%s profile %s\n", 0 + inc, CHOICE
+ }
}
- }' "$MERGED" > "$NEW_PROFILE"
- mv "$NEW_PROFILE" "$PROFILE"
- printf '%s\n' "$CHOICE"
-fi
+ ' > "$NEXT_PROFILE"
+
+mv "$NEXT_PROFILE" "$PROFILE"
+printf '%s\n' "$CHOICE"
diff --git a/tests/cli-opts.sh b/tests/cli-opts.sh
index ca2998a..ebaf18e 100755
--- a/tests/cli-opts.sh
+++ b/tests/cli-opts.sh
@@ -89,7 +89,7 @@ test_valid_options() {
printf 'a\nb\nc\n' | \
./src/remembering \
-p "always-unique-$(uuid)" \
- -c 'head -n1' \
+ -- head -n1 \
1>"$OUT" 2>"$ERR"
STATUS=$?
assert_status 0
@@ -176,13 +176,13 @@ test_environment_variables_and_precedence() {
printf 'a\n' | \
XDG_DATA_HOME="$XDG" ./src/remembering \
-p "$PROFILE" \
- -c 'head -n1' \
+ -- head -n1 \
1>"$OUT" 2>"$ERR"
STATUS=$?
assert_status 0
assert_empty_stderr
assert_stdout 'a'
- assert_stream "\$XDG PROFILE" "$XDG/remembering/$PROFILE" '1:a'
+ assert_stream "\$XDG PROFILE" "$XDG/remembering/$PROFILE" '1 profile a'
N="$LINENO"
OUT="$(mktemp)"
@@ -192,13 +192,13 @@ test_environment_variables_and_precedence() {
printf 'b\n' | \
HOME="$HHOME" XDG_DATA_HOME='' ./src/remembering \
-p "$PROFILE" \
- -c 'head -n1' \
+ -- head -n1 \
1>"$OUT" 2>"$ERR"
STATUS=$?
assert_status 0
assert_empty_stderr
assert_stdout 'b'
- assert_stream "\$HHOME PROFILE" "$HHOME/.local/share/remembering/$PROFILE" '1:b'
+ assert_stream "\$HHOME PROFILE" "$HHOME/.local/share/remembering/$PROFILE" '1 profile b'
N="$LINENO"
OUT="$(mktemp)"
@@ -209,14 +209,14 @@ test_environment_variables_and_precedence() {
printf 'c\n' | \
HOME="$HHOME" XDG_DATA_HOME="$XDG" ./src/remembering \
-p "$PROFILE" \
- -c 'head -n1' \
+ -- head -n1 \
1>"$OUT" 2>"$ERR"
STATUS=$?
assert_status 0
assert_empty_stderr
assert_stdout 'c'
assert_stream \
- "\$XDG and \$HOME PROFILE" "$XDG/remembering/$PROFILE" '1:c'
+ "\$XDG and \$HOME PROFILE" "$XDG/remembering/$PROFILE" '1 profile c'
if [ -e "$HHOME/.local/share/remembering/$PROFILE" ]; then
printf "\nERR: The file in \$HHOME/.local should't exist\n" >&2
diff --git a/tests/ranking.sh b/tests/ranking.sh
index 6649156..ea4365d 100755
--- a/tests/ranking.sh
+++ b/tests/ranking.sh
@@ -40,7 +40,7 @@ pick_x() {
echo "${2:-$INPUT}" | \
./src/remembering \
-p "$PROFILE" \
- -c "tee -a /dev/stderr | grep \"$PICK\"" \
+ -- sh -c "tee -a /dev/stderr | grep -F \"$PICK\"" \
1>"$OUT" 2>"$ERR"
STATUS=$?
assert_status 0
@@ -56,26 +56,28 @@ assert_first() {
fi
}
-BASE_PROFILE='0:a
-0:b
-0:c
-0:d
-0:e'
-BASE_PROFILE_A_PICKED='1:a
-0:b
-0:c
-0:d
-0:e'
+BASE_PROFILE='0 profile a
+0 profile b
+0 profile c
+0 profile d
+0 profile e'
+BASE_PROFILE_A_PICKED='1 profile a
+0 profile b
+0 profile c
+0 profile d
+0 profile e'
assert_profile() {
if [ "$(cat "$XDG_DATA_HOME/remembering/$1")" != "$2" ]; then
printf '\n%s: Bad profile merge (%s).\n\nExpected:\n%s\nGot\n%s\n' \
"$(ERROR)" "$PROFILE" "$2" "$(cat "$XDG_DATA_HOME/remembering/$1")" >&2
+ print_debug_info
exit 1
fi
}
test_picking_first_makes_it_be_always_first() {
testing 'picking first makes it be always first'
+ N="$LINENO"
OUT="$(mktemp)"
ERR="$(mktemp)"
PROFILE="always-picks-first-$(uuid)"
@@ -83,7 +85,7 @@ test_picking_first_makes_it_be_always_first() {
printf 'always-picked\nnever-picked\n' | \
./src/remembering \
-p "$PROFILE" \
- -c 'head -n1' \
+ -- head -n1 \
1>"$OUT" 2>"$ERR"
STATUS=$?
assert_status 0
@@ -95,6 +97,7 @@ test_picking_first_makes_it_be_always_first() {
test_promoting_values() {
testing 'promoting values'
+ N="$LINENO"
PROFILE="promoting-$(uuid)"
pick_x h
@@ -109,6 +112,7 @@ test_promoting_values() {
test_higher_values_loose_tie() {
testing 'higher values loose tie'
+ N="$LINENO"
PROFILE="higher-loose-tie-$(uuid)"
pick_x f
@@ -122,6 +126,7 @@ test_higher_values_loose_tie() {
test_smaller_values_win_tie() {
testing 'smaller values win tie'
+ N="$LINENO"
PROFILE="smaller-win-tie-$(uuid)"
pick_x d
@@ -135,6 +140,7 @@ test_smaller_values_win_tie() {
test_many_sequential_picks() {
testing 'many sequential pick'
+ N="$LINENO"
PROFILE="many-sequential-picks-$(uuid)"
pick_x b
@@ -181,14 +187,15 @@ s'
test_pick_inexisting_value() {
testing 'pick inexisting value'
+ N="$LINENO"
OUT="$(mktemp)"
ERR="$(mktemp)"
PROFILE="pick-inexisting-value-$(uuid)"
- echo '0:a
-0:b
-0:c
-0:d
-0:e' > "$XDG_DATA_HOME/remembering/$PROFILE"
+ echo '0 profile a
+0 profile b
+0 profile c
+0 profile d
+0 profile e' > "$XDG_DATA_HOME/remembering/$PROFILE"
INPUT='a
b
c
@@ -198,46 +205,48 @@ e'
echo "$INPUT" | \
./src/remembering \
-p "$PROFILE" \
- -c 'echo f' \
- 1>"$OUT" 2>"$ERR"
+ -- echo f \
+ 1>"$OUT" # 2>"$ERR"
STATUS=$?
assert_status 0
assert_stdout f
- assert_profile "$PROFILE" '1:f
-0:a
-0:b
-0:c
-0:d
-0:e'
-
+ assert_profile "$PROFILE" '0 profile a
+0 profile b
+0 profile c
+0 profile d
+0 profile e
+1 profile f'
test_ok
}
test_stdin_profile_merging() {
testing 'STDIN/profile merging'
+ N="$LINENO"
PROFILE="stdin-profile-merging-$(uuid)"
- echo '0:a
-0:b
-0:c
-0:z' > "$XDG_DATA_HOME/remembering/$PROFILE"
+ echo '0 profile a
+0 profile b
+0 profile c
+0 profile z' > "$XDG_DATA_HOME/remembering/$PROFILE"
INPUT='a
b
c
d
e'
+ EXPECTED='a
+b
+c
+d
+e'
pick_x a "$INPUT"
- if [ "$(cat "$ERR")" != "$INPUT" ]; then
- printf '\nERR: Bad profile merge.\n\nExpected:\n%s\nGot:\n%s\n' \
- "$INPUT" "$(cat "$ERR")" >&2
- exit 1
- fi
+ assert_stderr "$EXPECTED"
test_ok
}
test_stdin_is_larger_than_profile() {
testing 'STDIN is larger than profile'
+ N="$LINENO"
PROFILE="stdin-is-larger-than-profile-$(uuid)"
- echo '0:a' > "$XDG_DATA_HOME/remembering/$PROFILE"
+ echo '0 profile a' > "$XDG_DATA_HOME/remembering/$PROFILE"
INPUT='a
b
c
@@ -250,6 +259,7 @@ e'
test_stdin_is_smaller_than_profile() {
testing 'STDIN is smaller than profile'
+ N="$LINENO"
PROFILE="stdin-is-smaller-than-profile-$(uuid)"
echo "$BASE_PROFILE" > "$XDG_DATA_HOME/remembering/$PROFILE"
INPUT='a'
@@ -260,6 +270,7 @@ test_stdin_is_smaller_than_profile() {
test_stdin_is_empty() {
testing 'STDIN is empty'
+ N="$LINENO"
PROFILE="stdin-is-empty-$(uuid)"
echo "$BASE_PROFILE" > "$XDG_DATA_HOME/remembering/$PROFILE"
OUT="$(mktemp)"
@@ -268,11 +279,12 @@ test_stdin_is_empty() {
printf '' | \
./src/remembering \
-p "$PROFILE" \
- -c 'tee -a /dev/stderr | head -n1' \
+ -- sh -c 'tee -a /dev/stderr | head -n1' \
1>"$OUT" 2>"$ERR"
STATUS=$?
assert_status 0
+ assert_stderr ''
assert_stdout ''
assert_profile "$PROFILE" "$BASE_PROFILE"
test_ok
@@ -280,6 +292,7 @@ test_stdin_is_empty() {
test_profile_does_not_exist() {
testing 'profile does not exist'
+ N="$LINENO"
PROFILE="profile-does-not-exist-$(uuid)"
INPUT='a
b
@@ -293,6 +306,7 @@ e'
test_profile_is_empty() {
testing 'profile is empty'
+ N="$LINENO"
PROFILE="profile-is-empty-$(uuid)"
printf '' > "$XDG_DATA_HOME/remembering/$PROFILE"
INPUT='a
@@ -307,11 +321,12 @@ e'
test_names_with_spaces() {
testing 'names with spaces'
+ N="$LINENO"
PROFILE="names-with-spaces-$(uuid)"
INPUT='a b c
d e f'
- EXPECTED='1:a b c
-0:d e f'
+ EXPECTED='1 profile a b c
+0 profile d e f'
pick_x 'a b c' "$INPUT"
assert_profile "$PROFILE" "$EXPECTED"
test_ok
@@ -319,13 +334,14 @@ d e f'
test_really_long_list() {
testing 'really long list'
+ N="$LINENO"
OUT="$(mktemp)"
ERR="$(mktemp)"
PROFILE="really-long-list-$(uuid)"
seq 999999 | \
./src/remembering \
-p "$PROFILE" \
- -c 'head -n1' \
+ -- head -n1 \
1>"$OUT" 2>"$ERR"
STATUS=$?
assert_status 0
@@ -336,15 +352,16 @@ test_really_long_list() {
test_utf8_commands() {
testing 'UTF-8 commands'
+ N="$LINENO"
PROFILE="utf8-commands-$(uuid)"
INPUT='❤️
á
è
ŭ 😀'
- EXPECTED='0:á
-0:è
-1:ŭ 😀
-0:❤️'
+ EXPECTED='0 profile á
+0 profile è
+1 profile ŭ 😀
+0 profile ❤️'
pick_x 'ŭ 😀' "$INPUT"
assert_profile "$PROFILE" "$EXPECTED"
test_ok
@@ -355,7 +372,7 @@ test_promoting_values
test_higher_values_loose_tie
test_smaller_values_win_tie
test_many_sequential_picks
-# test_pick_inexisting_value
+test_pick_inexisting_value
test_stdin_profile_merging
test_stdin_is_larger_than_profile
test_stdin_is_smaller_than_profile
@@ -363,5 +380,5 @@ test_stdin_is_empty
test_profile_does_not_exist
test_profile_is_empty
test_names_with_spaces
-# test_really_long_list
+test_really_long_list
test_utf8_commands
diff --git a/tests/signals.sh b/tests/signals.sh
index 28b2e89..ff684cc 100755
--- a/tests/signals.sh
+++ b/tests/signals.sh
@@ -3,19 +3,21 @@ set -u
. tests/lib.sh
-export XDG_DATA_HOME="$PWD/tests/test-profiles"
+export XDG_DATA_HOME="$PWD/tests/test-profiles/signals-$(uuid)"
test_status_is_zero_when_command_is_successful() {
testing 'status is 0 when command is successful'
- printf 'a\n' | ./src/remembering -pp1 -c 'head -n1' 1>/dev/null 2>/dev/null
+ N="$LINENO"
+
+ printf 'a\n' | ./src/remembering -pp1 -- head -n1 1>/dev/null 2>/dev/null
STATUS=$?
assert_status 0
- printf '' | ./src/remembering -pp2 -c 'exit 0' 1>/dev/null 2>/dev/null
+ printf '' | ./src/remembering -pp2 -- true 1>/dev/null 2>/dev/null
STATUS=$?
assert_status 0
- seq 9 | ./src/remembering -pp3 -c 'grep 7' 1>/dev/null 2>/dev/null
+ seq 9 | ./src/remembering -pp3 -- grep 7 1>/dev/null 2>/dev/null
STATUS=$?
assert_status 0
@@ -24,8 +26,9 @@ test_status_is_zero_when_command_is_successful() {
test_status_is_forwarded_from_command() {
testing 'status is forwarded from command'
+ N="$LINENO"
for status in $(seq 1 125); do
- printf '' | ./src/remembering -pp4 -c "exit $status" 1>/dev/null 2>/dev/null
+ printf '' | ./src/remembering -pp4 -- sh -c "exit $status" 1>/dev/null 2>/dev/null
STATUS=$?
assert_status "$status"
done