#!/usr/bin/env bash

test_description='archive git'

. lib/test-lib.sh

PGIT_REQ=2.0
newest() {
    echo -e "$1\n$2" | sort -V | tail -n1
}
if ! PGIT_VERS=$($PYTHON -c 'import git; print(git.__version__)') ; then
    echo " MISSING PYTHON PACKAGE: git"
    echo " TEST SKIPPED"
    test_done
fi
if [[ $(newest "$PGIT_VERS" "$PGIT_REQ") == "$PGIT_REQ" ]] ; then
    echo " VERSION INCOMPATIBILITY: $PYTHON git ($PGIT_VERS < $PGIT_REQ)"
    echo " TEST SKIPPED"
    test_done
fi

export GUARD_ARCHIVE_ROOT="$TMP_DIRECTORY"/node_repos

################################################################

test_begin_subtest "commit logs"
$PYTHON - <<EOF >COMMIT
import os
from guardian import GuardSystem
from guardian.archive import SystemArchiveGit
sys = GuardSystem('TEST0')
sys.load()
archive_path = os.getenv('GUARD_ARCHIVE_ROOT')
archive_path = os.path.join(archive_path, sys.name)
with SystemArchiveGit(archive_path) as repo:
  msg0 = 'COMMIT 0'
  id0 = repo.commit(sys, msg0)
  # try another commit with no changes to make sure no commit is
  # registered
  msg1 = 'COMMIT 1'
  id1 = repo.commit(sys, msg1)
print(id0, msg0)
EOF
(cd "$GUARD_ARCHIVE_ROOT"/TEST0 \
	&& git log --reverse --pretty=oneline) >INSPECT
test_expect_equal_file COMMIT INSPECT

test_begin_subtest "commit system"
> TEST1.py
$PYTHON - <<EOF
import os
from guardian import GuardSystem
from guardian.archive import SystemArchiveGit
sys = GuardSystem('./TEST1.py')
archive_path = os.getenv('GUARD_ARCHIVE_ROOT')
archive_path = os.path.join(archive_path, sys.name)
sys.load()
with SystemArchiveGit(archive_path) as repo:
  repo.commit(sys, 'COMMIT 0')
EOF
(cd "$GUARD_ARCHIVE_ROOT"/TEST1 \
	&& git ls-files | sed "s|${TMP_DIRECTORY#/}|TMP_DIR|") \
    >OUTPUT
echo >>OUTPUT
cat "$GUARD_ARCHIVE_ROOT"/TEST1/GUARD_MODULE_PATH \
    | sed -e "s|${TMP_DIRECTORY#/}|TMP_DIR|" -e "s|${GUARD_MODULE_PATH#/}|MOD_PATH|" \
	  >>OUTPUT
cat <<EOF >EXPECTED
GUARD_MODULE_PATH
STATES
TMP_DIR/TEST1.py

TMP_DIR:MOD_PATH
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "commit loaded userapps code"
cat <<EOF >foo.py
a = 2
EOF
mkdir bar
touch bar/__init__.py
cat <<EOF >bar/baz.py
b = 5
EOF
cat <<EOF >TEST1.py
import sys
import foo
from bar import baz
from guardian import GuardState

class STATE1(GuardState):
    index = 50
EOF
$PYTHON - <<EOF
import os
from guardian import GuardSystem
from guardian.archive import SystemArchiveGit
sys = GuardSystem('./TEST1.py')
archive_path = os.getenv('GUARD_ARCHIVE_ROOT')
archive_path = os.path.join(archive_path, sys.name)
sys.load()
with SystemArchiveGit(archive_path) as repo:
  repo.commit(sys, 'COMMIT 1')
EOF
(cd "$GUARD_ARCHIVE_ROOT"/TEST1 \
	&& git ls-files | sed "s|${TMP_DIRECTORY#/}|TMP_DIR|" | sort) >OUTPUT
cat <<EOF >EXPECTED
GUARD_MODULE_PATH
STATES
TMP_DIR/TEST1.py
TMP_DIR/bar/__init__.py
TMP_DIR/bar/baz.py
TMP_DIR/foo.py
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "check state file 1"
cat <<EOF >EXPECTED
# state index requestable
STATE1 50 True
INIT 0 True
EOF
test_expect_equal_file "$GUARD_ARCHIVE_ROOT"/TEST1/STATES EXPECTED

test_begin_subtest "remove file"
cat <<EOF >TEST1.py
import sys
from bar import baz
from guardian import GuardState

class STATE1(GuardState):
    index = 50

class STATE2(GuardState):
    request = False
    index = 51
EOF
$PYTHON - <<EOF
import os
from guardian import GuardSystem
from guardian.archive import SystemArchiveGit
sys = GuardSystem('./TEST1.py')
archive_path = os.getenv('GUARD_ARCHIVE_ROOT')
archive_path = os.path.join(archive_path, sys.name)
sys.load()
with SystemArchiveGit(archive_path) as repo:
  repo.commit(sys, 'COMMIT 2')
EOF
(cd "$GUARD_ARCHIVE_ROOT"/TEST1 \
	&& git ls-files | sed "s|${TMP_DIRECTORY#/}|TMP_DIR|" | sort) >OUTPUT
cat <<EOF >EXPECTED
GUARD_MODULE_PATH
STATES
TMP_DIR/TEST1.py
TMP_DIR/bar/__init__.py
TMP_DIR/bar/baz.py
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "check state file 2"
cat <<EOF >EXPECTED
# state index requestable
STATE2 51 False
STATE1 50 True
INIT 0 True
EOF
test_expect_equal_file "$GUARD_ARCHIVE_ROOT"/TEST1/STATES EXPECTED

test_begin_subtest "log stat"
(cd "$GUARD_ARCHIVE_ROOT"/TEST1 \
	&& git log --numstat --format=format:'%s' \
	       | sed "s|${TMP_DIRECTORY#/}|TMP_DIR|") >OUTPUT
cat <<EOF >EXPECTED
COMMIT 2
1	0	STATES
4	1	TMP_DIR/TEST1.py
0	1	TMP_DIR/foo.py

COMMIT 1
1	0	STATES
7	0	TMP_DIR/TEST1.py
0	0	TMP_DIR/bar/__init__.py
1	0	TMP_DIR/bar/baz.py
1	0	TMP_DIR/foo.py

COMMIT 0
1	0	GUARD_MODULE_PATH
2	0	STATES
0	0	TMP_DIR/TEST1.py
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "daemon init and reload"
guardian ./TEST1.py &
wait_node_ready TEST1
sleep 2
gitid0=$(caget -t T1:GRD-TEST1_ARCHIVE_ID)
gitid0=$(node_git_hex "$gitid0")
cat <<EOF >>TEST1.py
from guardian import GuardState
class FOO(GuardState):
    pass
EOF
caput T1:GRD-TEST1_LOAD True
sleep 1
cawait T1:GRD-TEST1_LOAD_STATUS DONE
gitid1=$(caget -t T1:GRD-TEST1_ARCHIVE_ID)
gitid1=$(node_git_hex "$gitid1")
(cd "$GUARD_ARCHIVE_ROOT"/TEST1 \
	&& git log -2 --numstat --format=format:'%h %s' \
	    | sed "s|${TMP_DIRECTORY#/}|TMP_DIR|" && echo) >OUTPUT
kill_jobs
cat <<EOF >EXPECTED
$gitid1 DAEMON RELOAD
1	0	STATES
3	0	TMP_DIR/TEST1.py

$gitid0 COMMIT 2
1	0	STATES
4	1	TMP_DIR/TEST1.py
0	1	TMP_DIR/foo.py

EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "reload no changes"
guardian ./TEST1.py &
wait_node_ready TEST1
sleep 2
gitid0=$(caget -t T1:GRD-TEST1_ARCHIVE_ID)
caput T1:GRD-TEST1_LOAD True
sleep 1
cawait T1:GRD-TEST1_LOAD_STATUS DONE
gitid1=$(caget -t T1:GRD-TEST1_ARCHIVE_ID)
kill_jobs
test_expect_equal "$gitid0" "$gitid1"

test_begin_subtest "request SPM snapshot"
testioc ${IFO}:TEST- &
sleep .1
guardian --op=PAUSE --ca-prefix=TEST --name=TEST TEST0 &
wait_node_ready TEST
caput -c T1:GRD-TEST_REQUEST C
caput -c T1:GRD-TEST_OP EXEC
cawait T1:GRD-TEST_STATE C
cawait T1:GRD-TEST_OK True
caput -c T1:GRD-TEST_SPM_SNAP True
cawait T1:GRD-TEST_SPM_SNAP False
sleep 1
kill_jobs
cat <<EOF >EXPECTED
T1:TEST-A 4
T1:TEST-B 3
T1:TEST-C 4.0
EOF
test_expect_equal_file $GUARD_ARCHIVE_ROOT/TEST/TEST.spm EXPECTED

################################################################

test_done
