aboutsummaryrefslogtreecommitdiff
path: root/test.py
diff options
context:
space:
mode:
Diffstat (limited to 'test.py')
-rwxr-xr-xtest.py243
1 files changed, 147 insertions, 96 deletions
diff --git a/test.py b/test.py
index 023b369..c7e32c5 100755
--- a/test.py
+++ b/test.py
@@ -52,11 +52,16 @@ scheduled_state_template = Template("""
"sender": "+49123456", "cmd": ["split", "3", "bob", "charlie"]}},
"changes": {"alice": [], "bob": [], "charlie": []}}""")
+
def run_bot(test, sender, cmd):
- msg = msg_template.substitute(sender=sender, msg=cmd).replace("\n", "\\n") + "\n"
- res = subprocess.run(["python3", "./geldschieberbot.py"], text=True, capture_output=True,
- # res = subprocess.run(["python3", "./bot.py"], text=True, capture_output=True,
- input=msg)
+ msg = msg_template.substitute(sender=sender, msg=cmd).replace("\n",
+ "\\n") + "\n"
+ res = subprocess.run(
+ ["python3", "./geldschieberbot.py"],
+ text=True,
+ capture_output=True,
+ # res = subprocess.run(["python3", "./bot.py"], text=True, capture_output=True,
+ input=msg)
if res.returncode != 0:
print(res.stdout)
@@ -65,9 +70,11 @@ def run_bot(test, sender, cmd):
test.assertEqual(res.stderr, "")
return res
+
def save_state(dest):
copyfile(os.environ["GSB_STATE_FILE"], dest)
+
def reset_state(state=None):
if state:
copyfile(state, os.environ["GSB_STATE_FILE"])
@@ -76,9 +83,11 @@ def reset_state(state=None):
if os.path.isfile(state):
os.remove(state)
+
def reset_state_string(string):
- with open(os.environ["GSB_STATE_FILE"], "w") as f:
- json.dump(json.loads(string), f)
+ with open(os.environ["GSB_STATE_FILE"], "w") as f:
+ json.dump(json.loads(string), f)
+
def compare_state(comp_state):
with open(comp_state, "r") as csf, \
@@ -87,20 +96,20 @@ def compare_state(comp_state):
s = sf.read()
return cs == s
-class TestRegCmd(unittest.TestCase):
+class TestRegCmd(unittest.TestCase):
def setUp(self):
reset_state()
def test_correct_reg(self):
- res = run_bot(self, num[alice], "!reg "+alice)
+ res = run_bot(self, num[alice], "!reg " + alice)
self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(alice))
def test_double_reg(self):
- res = run_bot(self, num[alice], "!reg "+alice)
+ res = run_bot(self, num[alice], "!reg " + alice)
self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(alice))
- res = run_bot(self, num[alice], "!reg "+alice)
- self.assertEqual(res.stdout, 'ERROR: '+alice+' already registered')
+ res = run_bot(self, num[alice], "!reg " + alice)
+ self.assertEqual(res.stdout, 'ERROR: ' + alice + ' already registered')
def test_invalid_reg(self):
res = run_bot(self, num[alice], "!reg nase 03")
@@ -108,24 +117,25 @@ class TestRegCmd(unittest.TestCase):
def test_numerical_reg(self):
res = run_bot(self, num[alice], "!reg 9")
- self.assertEqual(res.stdout, 'ERROR: pure numerical names are not allowed')
+ self.assertEqual(res.stdout,
+ 'ERROR: pure numerical names are not allowed')
res = run_bot(self, num[alice], "!reg 9.7")
- self.assertEqual(res.stdout, 'ERROR: pure numerical names are not allowed')
+ self.assertEqual(res.stdout,
+ 'ERROR: pure numerical names are not allowed')
def test_additional_reg(self):
- res = run_bot(self, num[alice], "!reg "+alice)
+ res = run_bot(self, num[alice], "!reg " + alice)
self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(alice))
- res = run_bot(self, num[bob], "!reg "+bob)
+ res = run_bot(self, num[bob], "!reg " + bob)
self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(bob))
- res = run_bot(self, num[charlie], "!reg "+charlie)
+ res = run_bot(self, num[charlie], "!reg " + charlie)
self.assertEqual(res.stdout, 'Happy geldschiebing {}!'.format(charlie))
self.assertTrue(compare_state("test/state.json_3users"))
class TestTransactionCmd(unittest.TestCase):
-
@classmethod
def tearDownClass(cls):
reset_state()
@@ -134,92 +144,118 @@ class TestTransactionCmd(unittest.TestCase):
reset_state("test/state.json_3users")
def test_correct_schieb(self):
- res = run_bot(self, num[alice], "!schieb 10 "+bob)
- self.assertEqual(res.stdout, 'New Balance: {} <- 10.00 {}\n'.format(alice, bob))
+ res = run_bot(self, num[alice], "!schieb 10 " + bob)
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 10.00 {}\n'.format(alice, bob))
- res = run_bot(self, num[bob], "!schieb 10 "+alice)
- self.assertEqual(res.stdout, 'New Balance: {} <- 0.00 {}\n'.format(bob, alice))
+ res = run_bot(self, num[bob], "!schieb 10 " + alice)
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 0.00 {}\n'.format(bob, alice))
def test_correct_gib(self):
- res = run_bot(self, num[alice], "!gib 10 "+bob)
- self.assertEqual(res.stdout, 'New Balance: {} <- 10.00 {}\n'.format(alice, bob))
+ res = run_bot(self, num[alice], "!gib 10 " + bob)
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 10.00 {}\n'.format(alice, bob))
- res = run_bot(self, num[bob], "!gib 10 "+alice)
- self.assertEqual(res.stdout, 'New Balance: {} <- 0.00 {}\n'.format(bob, alice))
+ res = run_bot(self, num[bob], "!gib 10 " + alice)
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 0.00 {}\n'.format(bob, alice))
def test_correct_amount(self):
- res = run_bot(self, num[bob], "!schieb 1.1 "+alice)
- self.assertEqual(res.stdout, 'New Balance: {} <- 1.10 {}\n'.format(bob, alice))
+ res = run_bot(self, num[bob], "!schieb 1.1 " + alice)
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 1.10 {}\n'.format(bob, alice))
- res = run_bot(self, num[bob], "!gib 1,1 "+alice)
- self.assertEqual(res.stdout, 'New Balance: {} <- 2.20 {}\n'.format(bob, alice))
+ res = run_bot(self, num[bob], "!gib 1,1 " + alice)
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 2.20 {}\n'.format(bob, alice))
def test_invalid_amount(self):
- res = run_bot(self, num[bob], "!schieb 1b1 "+alice)
+ res = run_bot(self, num[bob], "!schieb 1b1 " + alice)
self.assertEqual(res.stdout, 'ERROR: amount must be a positive number')
- res = run_bot(self, num[bob], "!schieb ä€ "+alice)
+ res = run_bot(self, num[bob], "!schieb ä€ " + alice)
self.assertEqual(res.stdout, 'ERROR: amount must be a positive number')
def test_correct_schieb_name_before_amount(self):
- res = run_bot(self, num[alice], "!schieb "+bob+ " 10")
- self.assertEqual(res.stdout, 'New Balance: {} <- 10.00 {}\n'.format(alice, bob))
+ res = run_bot(self, num[alice], "!schieb " + bob + " 10")
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 10.00 {}\n'.format(alice, bob))
- res = run_bot(self, num[bob], "!schieb "+alice+ " 10")
- self.assertEqual(res.stdout, 'New Balance: {} <- 0.00 {}\n'.format(bob, alice))
+ res = run_bot(self, num[bob], "!schieb " + alice + " 10")
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 0.00 {}\n'.format(bob, alice))
def test_correct_gib_name_before_amount(self):
- res = run_bot(self, num[alice], "!gib "+charlie+ " 10")
- self.assertEqual(res.stdout, 'New Balance: {} <- 10.00 {}\n'.format(alice, charlie))
+ res = run_bot(self, num[alice], "!gib " + charlie + " 10")
+ self.assertEqual(
+ res.stdout, 'New Balance: {} <- 10.00 {}\n'.format(alice, charlie))
- res = run_bot(self, num[charlie], "!gib "+alice+ " 10")
- self.assertEqual(res.stdout, 'New Balance: {} <- 0.00 {}\n'.format(charlie, alice))
+ res = run_bot(self, num[charlie], "!gib " + alice + " 10")
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 0.00 {}\n'.format(charlie, alice))
def test_correct_nimm(self):
- res = run_bot(self, num[alice], "!nimm 10 "+bob)
- self.assertEqual(res.stdout, 'New Balance: {} -> 10.00 {}\n'.format(alice, bob))
+ res = run_bot(self, num[alice], "!nimm 10 " + bob)
+ self.assertEqual(res.stdout,
+ 'New Balance: {} -> 10.00 {}\n'.format(alice, bob))
- res = run_bot(self, num[bob], "!nimm 10 "+alice)
- self.assertEqual(res.stdout, 'New Balance: {} <- 0.00 {}\n'.format(bob, alice))
+ res = run_bot(self, num[bob], "!nimm 10 " + alice)
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 0.00 {}\n'.format(bob, alice))
def test_correct_zieh_name_before_amount(self):
- res = run_bot(self, num[alice], "!zieh "+charlie+ " 10")
- self.assertEqual(res.stdout, 'New Balance: {} -> 10.00 {}\n'.format(alice, charlie))
+ res = run_bot(self, num[alice], "!zieh " + charlie + " 10")
+ self.assertEqual(
+ res.stdout, 'New Balance: {} -> 10.00 {}\n'.format(alice, charlie))
- res = run_bot(self, num[charlie], "!zieh "+alice+ " 10")
- self.assertEqual(res.stdout, 'New Balance: {} <- 0.00 {}\n'.format(charlie, alice))
+ res = run_bot(self, num[charlie], "!zieh " + alice + " 10")
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 0.00 {}\n'.format(charlie, alice))
def test_transactions_complex(self):
- res = run_bot(self, num[alice], "!schieb "+charlie+ " 1,1")
- self.assertEqual(res.stdout, 'New Balance: {} <- 1.10 {}\n'.format(alice, charlie))
+ res = run_bot(self, num[alice], "!schieb " + charlie + " 1,1")
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 1.10 {}\n'.format(alice, charlie))
- res = run_bot(self, num[alice], "!zieh "+charlie+ " 2.1")
- self.assertEqual(res.stdout, 'New Balance: {} -> 1.00 {}\n'.format(alice, charlie))
+ res = run_bot(self, num[alice], "!zieh " + charlie + " 2.1")
+ self.assertEqual(res.stdout,
+ 'New Balance: {} -> 1.00 {}\n'.format(alice, charlie))
- res = run_bot(self, num[charlie], "!schieb "+bob+ " 42")
- self.assertEqual(res.stdout, 'New Balance: {} <- 42.00 {}\n'.format(charlie, bob))
+ res = run_bot(self, num[charlie], "!schieb " + bob + " 42")
+ self.assertEqual(res.stdout,
+ 'New Balance: {} <- 42.00 {}\n'.format(charlie, bob))
- res = run_bot(self, num[alice], "!zieh "+bob+ " 0.01")
- self.assertEqual(res.stdout, 'New Balance: {} -> 0.01 {}\n'.format(alice, bob))
+ res = run_bot(self, num[alice], "!zieh " + bob + " 0.01")
+ self.assertEqual(res.stdout,
+ 'New Balance: {} -> 0.01 {}\n'.format(alice, bob))
compare_state("test/state.json_transactions1")
def test_transactions_with_myself(self):
res = run_bot(self, num[alice], f"!schieb {alice} 1,1")
- self.assertEqual(res.stdout, 'ERROR: you can not transfer money to or from yourself')
+ self.assertEqual(
+ res.stdout,
+ 'ERROR: you can not transfer money to or from yourself')
res = run_bot(self, num[alice], f"!zieh {alice} 2.1")
- self.assertEqual(res.stdout, 'ERROR: you can not transfer money to or from yourself')
+ self.assertEqual(
+ res.stdout,
+ 'ERROR: you can not transfer money to or from yourself')
+
class TestSumCmd(unittest.TestCase):
def test_summary_single_user(self):
reset_state("test/state.json_transactions1")
- res = run_bot(self, num[alice], "!sum "+alice)
- self.assertEqual(res.stdout, 'Summary:\nalice:\n\t-> bob 0.01\n\t-> charlie 1.00\n\tBalance: -1.01\n')
+ res = run_bot(self, num[alice], "!sum " + alice)
+ self.assertEqual(
+ res.stdout,
+ 'Summary:\nalice:\n\t-> bob 0.01\n\t-> charlie 1.00\n\tBalance: -1.01\n'
+ )
def test_summary_invalide_single_user(self):
reset_state()
- res = run_bot(self, num[alice], "!sum "+alice)
+ res = run_bot(self, num[alice], "!sum " + alice)
self.assertEqual(res.stdout, 'ERROR: name "alice" not registered')
def test_summary_double_user(self):
@@ -257,11 +293,13 @@ charlie:
\tBalance: 43.00"""
self.assertEqual(res.stdout, summary)
-class TestMisc(unittest.TestCase):
+class TestMisc(unittest.TestCase):
def test_unknown_command(self):
res = run_bot(self, num[alice], "!foo")
- self.assertEqual(res.stdout, "ERROR: unknown cmd. Enter !help for a list of commands.")
+ self.assertEqual(
+ res.stdout,
+ "ERROR: unknown cmd. Enter !help for a list of commands.")
def test_no_command(self):
res = run_bot(self, num[alice], "Hi, how are you?")
@@ -269,22 +307,23 @@ class TestMisc(unittest.TestCase):
class TestListCmd(unittest.TestCase):
-
def setUp(self):
reset_state("test/state.json_3users")
def test_ls(self):
res = run_bot(self, num[alice], "!ls")
- msg = "alice: {}\nbob: {}\ncharlie: {}\n".format(num[alice], num[bob], num[charlie])
+ msg = "alice: {}\nbob: {}\ncharlie: {}\n".format(
+ num[alice], num[bob], num[charlie])
self.assertEqual(res.stdout, msg)
def test_list(self):
res = run_bot(self, num[bob], "!list")
- msg = "alice: {}\nbob: {}\ncharlie: {}\n".format(num[alice], num[bob], num[charlie])
+ msg = "alice: {}\nbob: {}\ncharlie: {}\n".format(
+ num[alice], num[bob], num[charlie])
self.assertEqual(res.stdout, msg)
-class TestSplitCmd(unittest.TestCase):
+class TestSplitCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -294,10 +333,12 @@ class TestSplitCmd(unittest.TestCase):
def test_split_invalid_args(self):
res = run_bot(self, num[alice], "!split")
- self.assertEqual(res.stdout, 'ERROR: not in form "!split amount [name]+"')
+ self.assertEqual(res.stdout,
+ 'ERROR: not in form "!split amount [name]+"')
res = run_bot(self, num[alice], "!split 10")
- self.assertEqual(res.stdout, 'ERROR: not in form "!split amount [name]+"')
+ self.assertEqual(res.stdout,
+ 'ERROR: not in form "!split amount [name]+"')
def test_split_one_unknown_user(self):
res = run_bot(self, num[alice], "!split 30 " + "foo" + " " + charlie)
@@ -330,7 +371,7 @@ alice:
\t<- charlie 20.00
\tBalance: 20.00"""
self.assertEqual(res.stdout, msg)
-
+
def test_split(self):
res = run_bot(self, num[alice], "!split 30 " + bob + " " + charlie)
msg = \
@@ -353,7 +394,8 @@ alice:
self.assertEqual(res.stdout, msg)
def test_split_whitespace(self):
- res = run_bot(self, num[alice], "!split 30 " + bob + " " + charlie + " ")
+ res = run_bot(self, num[alice],
+ "!split 30 " + bob + " " + charlie + " ")
msg = \
"""Split 30.00 between 3 -> 10.00 each
New Balance:
@@ -363,6 +405,7 @@ alice:
\tBalance: 20.00"""
self.assertEqual(res.stdout, msg)
+
class TestCarsAddCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -372,7 +415,7 @@ class TestCarsAddCmd(unittest.TestCase):
res = run_bot(self, num[alice], i)
o = 'added "foo" as an available car'
self.assertEqual(res.stdout, o)
-
+
i = "!cars new bar 0.02"
res = run_bot(self, num[alice], i)
o = 'added "bar" as an available car'
@@ -385,23 +428,24 @@ class TestCarsAddCmd(unittest.TestCase):
res = run_bot(self, num[alice], i)
o = "ERROR: service-charge must be a positive number"
self.assertEqual(res.stdout, o)
-
+
i = "!cars new bar -5"
res = run_bot(self, num[alice], i)
o = "ERROR: service-charge must be a positive number"
self.assertEqual(res.stdout, o)
-
+
def test_add_name_conflict(self):
i = "!cars add foo 0.04"
res = run_bot(self, num[alice], i)
o = 'added "foo" as an available car'
self.assertEqual(res.stdout, o)
-
+
i = "!cars new alice 0.02"
res = run_bot(self, num[alice], i)
o = 'ERROR: A user named "alice" already exists. Please use a different name for this car'
self.assertEqual(res.stdout, o)
+
class TestCarsTransactions(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_2cars")
@@ -412,6 +456,7 @@ class TestCarsTransactions(unittest.TestCase):
o = "New Balance: alice <- 20.00 foo\n"
self.assertEqual(res.stdout, o)
+
class TestCarsSum(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_2cars")
@@ -425,6 +470,7 @@ foo:
"""
self.assertEqual(res.stdout, o)
+
class TestCarPayCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_2cars")
@@ -473,7 +519,6 @@ foo:
\tBalance: -10.00"""
self.assertEqual(res.stdout, o)
-
def test_alice_pays_half(self):
run_bot(self, num[bob], "!zieh foo 20")
run_bot(self, num[charlie], "!zieh foo 10")
@@ -496,6 +541,7 @@ foo:
\tBalance: 15.00"""
self.assertEqual(res.stdout, o)
+
class TestCarsListCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_2cars")
@@ -541,7 +587,7 @@ bar - service charge 2ct/km
bar:
\tAll fine :)"""
self.assertEqual(res.stdout, o)
-
+
def test_list_explicit_car(self):
i = "!cars ls foo"
res = run_bot(self, num[alice], i)
@@ -557,8 +603,8 @@ foo:
o = 'ERROR: "alice" is no available car\n'
self.assertEqual(res.stdout, o)
-class TestTankenCmd(unittest.TestCase):
+class TestTankenCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -687,7 +733,6 @@ Car foo:
class TestTransferCmd(unittest.TestCase):
-
def setUp(self):
reset_state("test/state.json_3users")
@@ -728,7 +773,6 @@ New Balance: bob -> 5.00 charlie
#TODO: tanken, transfer, cars pay
class TestListChangesCmd(unittest.TestCase):
-
def setUp(self):
reset_state("test/state.json_3users")
@@ -806,8 +850,8 @@ Change 2:
"""
self.assertEqual(res.stdout, msg)
-class TestFuckCmd(unittest.TestCase):
+class TestFuckCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -897,7 +941,7 @@ alice <- 1.00 charlie
compare_state("test/state.json_3users")
def test_fuck_transaction(self):
- run_bot(self, num[alice], "!schieb "+bob+ " 10")
+ run_bot(self, num[alice], "!schieb " + bob + " 10")
res = run_bot(self, num[alice], "!fuck")
msg = \
"""alice: sorry I fucked up!
@@ -908,8 +952,8 @@ alice <- 10.00 bob
self.assertEqual(res.stdout, msg)
compare_state("test/state.json_3users")
-class TestScheduleCmd(unittest.TestCase):
+class TestScheduleCmd(unittest.TestCase):
def setUp(self):
reset_state("test/state.json_3users")
@@ -939,7 +983,9 @@ Cancelled the weekly cmd "stuff\""""
self.assertEqual(res.stdout, msg)
# Last exec onw week ago
- reset_state_string(scheduled_state_template.substitute(last_time=now-timedelta(7),schedule="weekly"))
+ reset_state_string(
+ scheduled_state_template.substitute(last_time=now - timedelta(7),
+ schedule="weekly"))
res = run_bot(self, num[alice], "!fuck")
# There is no new line because the real bot uses two Messages
@@ -959,7 +1005,9 @@ alice:
self.assertEqual(res.stdout, 'Nothing to rewind')
# Last exec two week ago
- reset_state_string(scheduled_state_template.substitute(last_time=now-timedelta(14),schedule="weekly"))
+ reset_state_string(
+ scheduled_state_template.substitute(last_time=now - timedelta(14),
+ schedule="weekly"))
res = run_bot(self, num[alice], "!fuck")
# There is no new line because the real bot uses two Messages
@@ -979,7 +1027,7 @@ alice:
\tBalance: 6.00""".format((now - timedelta(7)).isoformat(),
now.isoformat())
self.assertEqual(res.stdout, msg)
-
+
os.remove("test/state.json_schedule_weekly")
def test_monthly(self):
@@ -1010,11 +1058,12 @@ Cancelled the monthly cmd "stuff\""""
# Last exec one month ago
if now.month > 1:
- one_month_ago = date(now.year, now.month-1, now.day)
+ one_month_ago = date(now.year, now.month - 1, now.day)
else:
- one_month_ago = date(now.year-1, 12, now.day)
- reset_state_string(scheduled_state_template.substitute(last_time=one_month_ago,
- schedule="monthly"))
+ one_month_ago = date(now.year - 1, 12, now.day)
+ reset_state_string(
+ scheduled_state_template.substitute(last_time=one_month_ago,
+ schedule="monthly"))
res = run_bot(self, num[alice], "!fuck")
# There is no new line because the real bot uses two Messages
@@ -1035,12 +1084,13 @@ alice:
# Last exec two month ago
if now.month > 2:
- two_months_ago = date(now.year, now.month-2, now.day)
+ two_months_ago = date(now.year, now.month - 2, now.day)
else:
- two_months_ago = date(now.year-1, 12-now.month+1, now.day)
+ two_months_ago = date(now.year - 1, 12 - now.month + 1, now.day)
- reset_state_string(scheduled_state_template.substitute(last_time=two_months_ago,
- schedule="monthly"))
+ reset_state_string(
+ scheduled_state_template.substitute(last_time=two_months_ago,
+ schedule="monthly"))
res = run_bot(self, num[alice], "!fuck")
# There is no new line because the real bot uses to Messages
@@ -1060,13 +1110,13 @@ alice:
\tBalance: 6.00""".format(one_month_ago.isoformat(),
now.isoformat())
self.assertEqual(res.stdout, msg)
-
- os.remove("test/state.json_schedule_monthly")
+ os.remove("test/state.json_schedule_monthly")
def test_monthly_car(self):
run_bot(self, num[alice], "!cars add fiat 0.5")
- res = run_bot(self, num[alice], "!monthly versicherung cars pay fiat 3")
+ res = run_bot(self, num[alice],
+ "!monthly versicherung cars pay fiat 3")
msg = \
"""Recorded the monthly command "cars pay fiat 3" as "versicherung"
Running monthly command versicherung for alice initially
@@ -1083,5 +1133,6 @@ fiat:
\tBalance: -3.00"""
self.assertEqual(res.stdout, msg)
+
if __name__ == '__main__':
unittest.main()