Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration testing in CI #21

Merged
merged 4 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CI
on: [workflow_dispatch, push, pull_request]

jobs:
build:
build_and_test:
runs-on: ubuntu-latest

steps:
Expand All @@ -15,6 +15,10 @@ jobs:
auto-config: "false"
use-mathlib-cache: "false"
build: "true"
- name: Run integration tests
run: |
./test_daemon.py
# - name: Create package
# run: |
# ./create_release.sh
Expand Down
93 changes: 67 additions & 26 deletions test_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,90 @@
import fcntl
import subprocess

def main():
socket_path = "./test.sock"
daemon_command = "./.lake/build/bin/sand"
daemon_args = ["daemon"]
from contextlib import contextmanager

sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

# Remove the socket file if it already exists
SOCKET_PATH = "./test.sock"

'''
Remove the socket file if it already exists
'''
def ensure_socket_deleted():
try:
os.unlink(socket_path)
except OSError:
if os.path.exists(socket_path):
raise
os.unlink(SOCKET_PATH)
except FileNotFoundError:
pass

'''
Ensures when the context manager exits:
- the daemon process is terminated
- the socket file is removed

(assuming we're not killed with SIGKILL)
'''
@contextmanager
def daemon():
ensure_socket_deleted()

sock.bind(socket_path)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind(SOCKET_PATH)
sock.listen(1)

flags = fcntl.fcntl(sock.fileno(), fcntl.F_GETFD)
flags |= fcntl.FD_CLOEXEC
fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, flags)

print(f"Python: Socket created at {socket_path} on fd {sock.fileno()}")
print(f"-- Socket created at {SOCKET_PATH} on fd {sock.fileno()}")

daemon_proc = subprocess.Popen(
[daemon_command] + daemon_args,
pass_fds=(sock.fileno(),),
)
daemon_command = "./.lake/build/bin/sand"
daemon_args = ["daemon"]
try:
daemon_proc = subprocess.Popen(
[daemon_command] + daemon_args,
pass_fds=(sock.fileno(),),
)

print(f"Python: Daemon started with PID {daemon_proc.pid}")
print(f"-- Daemon started with PID {daemon_proc.pid}")
# Close the socket in the parent process
sock.close()
yield daemon_proc
finally:
print(f"-- Terminating daemon with PID {daemon_proc.pid}")
daemon_proc.terminate()
print(f"-- Waiting for daemon to terminate")
daemon_proc.wait()
print(f"-- Daemon terminated")
print(f"-- Removing socket file {SOCKET_PATH}")
ensure_socket_deleted()


# Close the socket in the parent process
sock.close()
def main():
print("--------------------------")
print("Starting integration tests")
print("--------------------------")

run_client_tests(socket_path)
with daemon() as daemon_proc:
run_client_tests()

def run_client_tests(socket_path):
print(f"Python: Running client tests against {socket_path}")
def run_client_tests():
print(f"-- Running client tests against {SOCKET_PATH}")

client_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client_sock.connect(socket_path)
client_sock.send(b'"list"')
client_sock.connect(SOCKET_PATH)

# test list
msg = b'"list"'
client_sock.send(msg)
response = client_sock.recv(1024)
print(f"Python: Received response: {response}")
expected = b'{"ok": {"timers": []}}'
if response != expected:
print(f"sent: {msg}")
print(f"expected: {expected}")
print(f"received: {response}")
sys.exit(1)

print("-------------------")
print("All tests passed")
print("-------------------")
client_sock.close()

if __name__ == "__main__":
Expand Down