#!/usr/bin/env bash

test_description='python system library'

. lib/test-lib.sh

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

test_begin_subtest "empty system"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem()
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(sys.states)
print(sys.goto_states)
print(sys.request_states)
print(len(sys))
for state in sys:
  print('foo')
EOF
cat <<EOF >EXPECTED
T1
None
None
[]
None
None
None
[]
[]
[]
0
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "load empty system"
$PYTHON - <<EOF >OUTPUT
import guardian
try:
 guardian.GuardSystem().load()
except guardian.system.GuardSystemLoadError as e:
 print(e)
EOF
cat <<EOF >EXPECTED
No module specified for this system.
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "basic system by module name"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem('TEST0')
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sys.states)
print(sys.goto_states)
print(sys.request_states)
print(sys.edges)
print()
sys.load()
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sorted(sys.states))
print(sorted(sys.goto_states))
print(sorted(sys.request_states))
print(sorted(sys.edges))
EOF
cat <<EOF >EXPECTED
T1
TEST0
MOD_PATH/TEST0.py
[]
['MOD_PATH']
None
None
None
0
[]
[]
[]
[]

T1
TEST0
MOD_PATH/TEST0.py
[]
['MOD_PATH']
None
None
None
5
['A', 'B', 'C', 'INIT', 'J']
['A']
['A', 'C', 'INIT']
[('A', 'B'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('INIT', 'A'), ('J', 'A')]
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "state name check"
$PYTHON - <<EOF >OUTPUT
import guardian
try:
 guardian.GuardSystem("$GUARD_MODULE_PATH/TEST_longname.py").load()
except guardian.system.GuardSystemLoadError as e:
 print(e)
EOF
cat <<EOF >EXPECTED
state name over 39 character limit: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (41 characters)
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "state goto check"
$PYTHON - <<EOF >OUTPUT
import guardian
try:
 guardian.GuardSystem("$GUARD_MODULE_PATH/TEST_gotobool.py").load()
except guardian.system.GuardSystemLoadError as e:
 print(e)
EOF
cat <<EOF >EXPECTED
state 'goto' attribute must be a bool or int: A
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "state request check"
$PYTHON - <<EOF >OUTPUT
import guardian
try:
 guardian.GuardSystem("$GUARD_MODULE_PATH/TEST_requestbool.py").load()
except guardian.system.GuardSystemLoadError as e:
 print(e)
EOF
cat <<EOF >EXPECTED
state 'request' attribute must be a bool: A
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "basic system by module path"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem('$GUARD_MODULE_PATH/TEST0.py')
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys.states))
print(sys.states)
print(sys.goto_states)
print(sys.request_states)
print(sys.edges)
print()
sys.load()
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sorted(sys.states))
print(sorted(sys.goto_states))
print(sorted(sys.request_states))
print(sorted(sys.edges))
EOF
cat <<EOF >EXPECTED
T1
TEST0
MOD_PATH/TEST0.py
[]
['MOD_PATH']
None
None
None
0
[]
[]
[]
[]

T1
TEST0
MOD_PATH/TEST0.py
[]
['MOD_PATH']
None
None
None
5
['A', 'B', 'C', 'INIT', 'J']
['A']
['A', 'C', 'INIT']
[('A', 'B'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('INIT', 'A'), ('J', 'A')]
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "basic system by relative path"
cp "$GUARD_MODULE_PATH"/TEST0.py .
$PYTHON - <<EOF | sed -e "s|$GUARD_MODULE_PATH|MOD_PATH|" -e "s|$TMP_DIRECTORY|TMP_DIR|" >OUTPUT
import guardian
sys = guardian.GuardSystem('./TEST0.py')
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sys.states)
print(sys.goto_states)
print(sys.request_states)
print(sys.edges)
print()
sys.load()
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sorted(sys.states))
print(sorted(sys.goto_states))
print(sorted(sys.request_states))
print(sorted(sys.edges))
EOF
rm -f TEST0.py
cat <<EOF >EXPECTED
T1
TEST0
TMP_DIR/TEST0.py
[]
['TMP_DIR', 'MOD_PATH']
None
None
None
0
[]
[]
[]
[]

T1
TEST0
TMP_DIR/TEST0.py
[]
['TMP_DIR', 'MOD_PATH']
None
None
None
5
['A', 'B', 'C', 'INIT', 'J']
['A']
['A', 'C', 'INIT']
[('A', 'B'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('INIT', 'A'), ('J', 'A')]
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "basic system, alt args"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem('TEST0', ifo='T2', name='TEST1', ca_prefix='TEST')
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sys.states)
print(sys.goto_states)
print(sys.request_states)
print(sys.edges)
print()
sys.load()
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sorted(sys.states))
print(sorted(sys.goto_states))
print(sorted(sys.request_states))
print(sorted(sys.edges))
EOF
cat <<EOF >EXPECTED
T2
TEST1
MOD_PATH/TEST0.py
[]
['MOD_PATH']
TEST
None
None
0
[]
[]
[]
[]

T2
TEST1
MOD_PATH/TEST0.py
[]
['MOD_PATH']
TEST
None
None
5
['A', 'B', 'C', 'INIT', 'J']
['A']
['A', 'C', 'INIT']
[('A', 'B'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('INIT', 'A'), ('J', 'A')]
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "inherited states"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem('TEST_basestate', name='TEST', ca_prefix='TEST')
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sys.states)
print(sys.goto_states)
print(sys.request_states)
print(sys.edges)
print()
sys.load()
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sorted(sys.states))
print(sorted(sys.goto_states))
print(sorted(sys.request_states))
print(sorted(sys.edges))
EOF
cat <<EOF >EXPECTED
T1
TEST
MOD_PATH/TEST_basestate.py
[]
['MOD_PATH']
TEST
None
None
0
[]
[]
[]
[]

T1
TEST
MOD_PATH/TEST_basestate.py
[]
['MOD_PATH']
TEST
None
None
3
['INIT', 'X', 'Y']
[]
['INIT', 'X', 'Y']
[]
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "inherited system"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem('TEST1')
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sys.states)
print(sys.goto_states)
print(sys.request_states)
print(sys.edges)
print()
sys.load()
print(sys.ifo)
print(sys.name)
print(sys.path)
print(sys.usercode)
print(sys.guardpath)
print(sys.ca_prefix)
print(sys.request)
print(sys.nominal)
print(len(sys))
print(sorted(sys.states))
print(sorted(sys.goto_states))
print(sorted(sys.request_states))
print(sorted(sys.edges))
EOF
cat <<EOF >EXPECTED
T1
TEST1
MOD_PATH/TEST1.py
[]
['MOD_PATH']
None
None
None
0
[]
[]
[]
[]

T1
TEST1
MOD_PATH/TEST1.py
['MOD_PATH/TEST0.py']
['MOD_PATH']
TEST-
A
D
6
['A', 'B', 'C', 'D', 'INIT', 'J']
['A']
['A', 'C', 'D', 'INIT']
[('A', 'B'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'D'), ('D', 'A'), ('INIT', 'A'), ('J', 'A')]
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "system magic methods"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem('TEST0')
sys.load()
print(sys)
print('A' in sys)
print('ASDF' in sys)
print(' '.join([state for state in sorted(sys)]))
print(len(sys))
EOF
cat <<EOF >EXPECTED
<GuardSystem 'TEST0'>
True
False
A B C INIT J
5
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "shortest path"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem('TEST1')
sys.load()
print(sys.shortest_path('INIT', 'D'))
print(sys.shortest_path(None, 'B'))
print(sys.shortest_path('A', 'B'))
print(sys.shortest_path('A', 'D'))
print(sys.shortest_path('B', 'D'))
print(sys.shortest_path('C', 'C'))
EOF
cat <<EOF >EXPECTED
['INIT', 'A', 'B', 'C', 'D']
['A', 'B']
['A', 'B']
['A', 'B', 'C', 'D']
['B', 'C', 'D']
['C']
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "state properties and methods"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem('TEST_decorators')
sys.load()
s = sys['B']()
print(s)
print(s.goto)
print(s.request)
print(s.main())
print(s.run())
EOF
cat <<EOF >EXPECTED
<GuardState 'B'>
False
True
B main
B run
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "state decorator"
$PYTHON - <<EOF | sed "s|$GUARD_MODULE_PATH|MOD_PATH|" >OUTPUT
import guardian
sys = guardian.GuardSystem('TEST_decorators')
sys.load()
c =  sys['C']()
print(c.main())
print(c.run())
d =  sys['D']()
print(d.main())
print(d.run())
e =  sys['E']()
print(e.main())
print(e.run())
EOF
cat <<EOF >EXPECTED
C pre
C pre
D pre_return
D pre_return
E main
E post
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "frozen graph"
$PYTHON - <<EOF >OUTPUT
import guardian
sys = guardian.GuardSystem('TEST0')
sys.load()
try:
 sys.add_state('FOO', guardian.GuardState)
except guardian.system.GuardSystemLoadError as e:
 print(e)
EOF
cat <<EOF >EXPECTED
system is frozen.
EOF
test_expect_equal_file OUTPUT EXPECTED

test_begin_subtest "module state removal"
cat <<EOF >TEST0.py
from guardian import GuardState
nominal = 'B'
class A(GuardState):
    pass
class B(GuardState):
    pass
EOF
cat <<EOF >TEST1.py
from guardian import GuardState
nominal = 'A'
class A(GuardState):
    pass
EOF
$PYTHON - <<EOF >OUTPUT
import guardian
import shutil
import time
sys = guardian.GuardSystem('./TEST0.py')
sys.load()
print(sorted(sys.states))
print(sys.nominal)
shutil.copyfile('TEST1.py', 'TEST0.py')
sys.load()
print(sorted(sys.states))
print(sys.nominal)
EOF
cat <<EOF >EXPECTED
['A', 'B', 'INIT']
B
['A', 'INIT']
A
EOF
test_expect_equal_file OUTPUT EXPECTED

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

test_done
