Skip to content

Commit 2a7cf4a

Browse files
authored
Setting up unittest automation (#84)
* Added tests * Trying to setup test automation * Double check * Another unit test try * What happens if I remove conda? * Could this be it? * LTL interps depend on arch? * Try this? * This? * Better? * Maybe this updates some things? * Better? * ReGen parser
1 parent 59373e3 commit 2a7cf4a

File tree

7 files changed

+164
-55
lines changed

7 files changed

+164
-55
lines changed

.github/workflows/unittest.yaml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Run Unit Tests
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main # Run tests on pull requests targeting the 'main' branch
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
# Step 1: Check out the code
14+
- name: Check out code
15+
uses: actions/checkout@v3
16+
17+
# Step 2: Build the Docker image
18+
- name: Build Docker image
19+
run: |
20+
docker build -t ltltutor .
21+
22+
# Step 3: Run tests inside the Docker container
23+
- name: Run tests in Docker container
24+
run: |
25+
docker run --rm ltltutor /bin/bash -c "source /venv/bin/activate && python -m unittest discover -s test -p 'test_*.py'"

runtests.sh

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
3+
# filepath: /home/sid/Desktop/ltltutor/run-tests.sh
4+
5+
6+
# Run the unittests
7+
python -m unittest discover -s test -p "test_*.py"

src/ltlnode.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
# Description: This file contains the classes for the nodes of the LTL syntax tree.
22

3+
import os
4+
5+
6+
### TODO: Ideally, this should not be in
7+
### src, but mocked in the test directory.
8+
9+
10+
from spotutils import areEquivalent
11+
12+
313
from ltlListener import ltlListener
414
from antlr4 import CommonTokenStream, ParseTreeWalker
515
from antlr4 import ParseTreeWalker, CommonTokenStream, InputStream
616
from ltlLexer import ltlLexer
717
from ltlParser import ltlParser
818
from abc import ABC, abstractmethod
9-
from spotutils import areEquivalent
10-
import random
1119
import ltltoeng
12-
import re
13-
1420

1521

1622
SUPPORTED_SYNTAXES = ['Classic', 'Forge', 'Electrum']
@@ -26,12 +32,6 @@
2632
FINALLY_SYMBOL = 'F'
2733
UNTIL_SYMBOL = 'U'
2834

29-
30-
## TODO: Too much is based on the Exact syntax. We should be able to
31-
## switch syntaxes. Should this be server side or client side?
32-
## If server side, we should have a way to switch syntaxes (__str__ vs __str__(syntax))
33-
34-
3535

3636
class LTLNode(ABC):
3737
def __init__(self, type):

src/spotutils.py

-7
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,6 @@
3232

3333

3434
def areEquivalent(formula1, formula2):
35-
36-
# Parse the formulas
37-
# f1 = spot.formula(str(formula1))
38-
# f2 = spot.formula(str(formula2))
39-
40-
# Check if they are equivalent
41-
#return spot.formula.are_equivalent(f1, f2)
4235
return isSufficientFor(formula1, formula2) and isNecessaryFor(formula1, formula2)
4336

4437

test/test_ltlnode.py

-36
This file was deleted.

test/test_codebook.py test/test_mutation.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
import unittest
2-
import codebook
2+
import sys
3+
import os
4+
5+
6+
# Add the src directory to sys.path
7+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../src")))
8+
9+
310
from ltlnode import parse_ltl_string
11+
import codebook
412

513

6-
class TestCodebook(unittest.TestCase):
14+
"""
15+
Test cases for the conceptual mutation operators in the LTL misconceptions codebook.
16+
"""
17+
class TestConceptualMutator(unittest.TestCase):
18+
719
def apply_and_check_misconception(self, code, test_cases):
820
"""
921
Helper function to test multiple input/output pairs for a given misconception code.

test/test_parsing.py

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import unittest
2+
import sys
3+
import os
4+
5+
# Add the src directory to sys.path
6+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../src")))
7+
8+
from ltlnode import *
9+
10+
11+
## LTL String --> AST Node
12+
class TestParseLTLString(unittest.TestCase):
13+
def test_parse_ltl_string(self):
14+
test_cases = [
15+
("a", LiteralNode),
16+
("(abc)", LiteralNode),
17+
("a & b", AndNode),
18+
("a | b", OrNode),
19+
("a & b", AndNode),
20+
("a | b", OrNode),
21+
("!a", NotNode),
22+
("a -> b", ImpliesNode),
23+
("a <-> b", EquivalenceNode),
24+
("X(a)", NextNode),
25+
("F(a)", FinallyNode),
26+
("G(a)", GloballyNode),
27+
("X (a & b)", NextNode),
28+
("(X a) & b", AndNode),
29+
("(X a) | b", OrNode),
30+
("G((X a) | b)", GloballyNode),
31+
("a U b", UntilNode),
32+
("a & (b | c)", AndNode),
33+
("(a & b) | c", OrNode)
34+
]
35+
36+
for input_str, expected_node in test_cases:
37+
with self.subTest(input=input_str, expected=expected_node):
38+
self.assertIsInstance(parse_ltl_string(input_str), expected_node)
39+
40+
41+
"""
42+
Tests for the correspondence between Forge and Classic LTL syntax.
43+
"""
44+
class TestForgeClassicSyntaxCorrespondence(unittest.TestCase):
45+
def test_parse_ltl_string(self):
46+
test_cases = [
47+
# Literals
48+
("a", "a"),
49+
# Conjunction
50+
("a & b", "(a & b)"),
51+
# Disjunction
52+
("a | b", "(a | b)"),
53+
# Negation
54+
("!a", "!a"),
55+
# Implication
56+
("a -> b", "(a -> b)"),
57+
# Equivalence
58+
("a <-> b", "(a <-> b)"),
59+
# Next
60+
("NEXT_STATE a", "(X a)"),
61+
# Eventually
62+
("EVENTUALLY a", "(F a)"),
63+
# Globally
64+
("ALWAYS a", "(G a)"),
65+
# Until
66+
("a UNTIL b", "(a U b)"),
67+
]
68+
69+
for forge, classic in test_cases:
70+
with self.subTest(input=forge, expected=classic):
71+
self.assertEqual(str(parse_ltl_string(forge)), str(parse_ltl_string(classic)))
72+
73+
"""
74+
Tests for the correspondence between Electrum and Classic LTL syntax.
75+
"""
76+
class TestElectrumClassicSyntaxCorrespondence(unittest.TestCase):
77+
def test_parse_ltl_string(self):
78+
test_cases = [
79+
# Literals
80+
("a", "a"),
81+
# Conjunction
82+
("a & b", "(a & b)"),
83+
# Disjunction
84+
("a | b", "(a | b)"),
85+
# Negation
86+
("!a", "!a"),
87+
# Implication
88+
("a -> b", "(a -> b)"),
89+
# Equivalence
90+
("a <-> b", "(a <-> b)"),
91+
# Next
92+
("AFTER a", "(X a)"),
93+
# Eventually
94+
("EVENTUALLY a", "(F a)"),
95+
# Globally
96+
("ALWAYS a", "(G a)"),
97+
# Until
98+
("a UNTIL b", "(a U b)"),
99+
]
100+
101+
for forge, classic in test_cases:
102+
with self.subTest(input=forge, expected=classic):
103+
self.assertEqual(str(parse_ltl_string(forge)), str(parse_ltl_string(classic)))
104+
105+
106+
107+
if __name__ == "__main__":
108+
unittest.main()

0 commit comments

Comments
 (0)