Files
revng-revng/tests/daemon/test.py
Giacomo Vercesi c32bc92f9c revng.daemon: introduce tests
Add end-to-end tests checking that every GraphQL endpoint is properly
working and returning the expected result
2022-04-26 15:05:30 +02:00

351 lines
7.4 KiB
Python
Executable File

#!/usr/bin/env python3
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
import io
import os
from random import randint
from subprocess import Popen
from time import sleep
from typing import Generator
from urllib.error import URLError
from urllib.request import urlopen
from gql import Client, gql
from gql.transport.requests import RequestsHTTPTransport
from pytest import Config, fixture, mark
def print_fd(fd: int):
os.lseek(fd, 0, io.SEEK_SET)
out_read = os.fdopen(fd, "r")
print(out_read.read())
def check_server_up(port: int):
for _ in range(10):
try:
req = urlopen(f"http://127.0.0.1:{port}/", timeout=1.0)
if req.code == 200:
return
sleep(1.0)
except (URLError, TimeoutError):
sleep(1.0)
raise ValueError()
@fixture
def client(pytestconfig: Config, request) -> Generator[Client, None, None]:
port = randint(20000, 65000)
root = pytestconfig.getoption("root")
out_fd = os.memfd_create("flask_debug", 0)
out = os.fdopen(out_fd, "w")
process = Popen(
[f"{root}/bin/revng", "daemon", "-p", str(port)], stdout=out, stderr=out, text=True
)
try:
check_server_up(port)
except ValueError as e:
print_fd(out_fd)
raise e
binary = pytestconfig.getoption("binary")
transport = RequestsHTTPTransport(f"http://127.0.0.1:{port}/graphql")
gql_client = Client(transport=transport, fetch_schema_from_transport=True)
upload_q = gql(
"""
mutation upload($file: Upload!) {
upload_file(file: $file, container: "input")
}
"""
)
with open(binary, "rb") as binary_file:
gql_client.execute(upload_q, variable_values={"file": binary_file}, upload_files=True)
yield gql_client
process.terminate()
process.wait()
if request.node.rep_call.failed:
print_fd(out_fd)
def test_info(client):
q = gql(
"""
{
info {
kinds {
name
rank
parent
}
ranks {
name
depth
parent
}
steps {
name
}
}
}
"""
)
result = client.execute(q)
binary_kind = next(k for k in result["info"]["kinds"] if k["name"] == "Binary")
isolated_kind = next(k for k in result["info"]["kinds"] if k["name"] == "IsolatedRoot")
assert binary_kind["rank"] == "root"
assert binary_kind["parent"] is None
assert isolated_kind["rank"] == "root"
assert isolated_kind["parent"] == "Root"
root_rank = next(r for r in result["info"]["ranks"] if r["name"] == "root")
function_rank = next(r for r in result["info"]["ranks"] if r["name"] == "function")
assert root_rank["depth"] == 0
assert root_rank["parent"] is None
assert function_rank["depth"] == 1
assert function_rank["parent"] == "root"
step_names = [s["name"] for s in result["info"]["steps"]]
assert "begin" in step_names
def test_lift(client):
q = gql(
"""
{
root {
lift
}
}
"""
)
result = client.execute(q)
assert result["root"]["lift"] is not None
@mark.xfail(raises=Exception)
def test_lift_ready_fail(client):
q = gql(
"""
{
root {
lift(only_if_ready: true)
}
}
"""
)
client.execute(q)
@mark.xfail(raises=Exception)
def test_invalid_step(client):
q = gql(
"""
{
step(name: "this_step_does_not_exist") {
name
}
}
"""
)
client.execute(q)
def test_valid_steps(client):
q = gql(
"""
{
begin: step(name: "begin") {
name
parent
}
lift: step(name: "Lift") {
name
parent
}
}
"""
)
result = client.execute(q)
assert result["begin"]["name"] == "begin"
assert result["begin"]["parent"] is None
assert result["lift"]["name"] == "Lift"
assert result["lift"]["parent"] == "begin"
def test_begin_has_containers(client):
q = gql(
"""
{
step(name: "begin") {
containers {
name
mime
}
}
container(name: "input", step: "begin") {
name
mime
}
}
"""
)
result = client.execute(q)
container_names = [c["name"] for c in result["step"]["containers"]]
input_container = next(c for c in result["step"]["containers"] if c["name"] == "input")
assert "module.ll" in container_names
assert "input" in container_names
assert input_container["mime"] is not None
assert input_container["mime"] == result["container"]["mime"]
def test_get_model(client):
client.execute(gql("{root{lift}}"))
q = gql(
"""
{
info {
model
}
}
"""
)
result = client.execute(q)
assert result["info"]["model"] is not None
def test_targets_from_step(client):
q = gql(
"""
{
begin: step(name: "begin") {
containers {
name
targets {
serialized
kind
ready
exact
}
}
}
lift: step(name: "Lift") {
containers {
name
targets {
kind
ready
}
}
}
}
"""
)
result = client.execute(q)
binary_target = next(
t
for t in next(c for c in result["begin"]["containers"] if c["name"] == "input")["targets"]
if t["kind"] == "Binary"
)
lift_target = next(
t
for t in next(c for c in result["lift"]["containers"] if c["name"] == "module.ll")[
"targets"
]
if t["kind"] == "Root"
)
assert binary_target["ready"]
assert binary_target["exact"]
assert not lift_target["ready"]
def test_targets(client):
q = gql(
"""
{
targets(pathspec: "") {
name
}
}
"""
)
result = client.execute(q)
names = [s["name"] for s in result["targets"]]
assert "begin" in names
def test_produce(client):
q = gql(
"""
{
produce(step: "Lift", container: "module.ll", target_list: ":Root")
}
"""
)
result = client.execute(q)
assert "produce" in result, result["produce"] != ""
def test_produce_artifact(client):
q = gql(
"""
{
produce_artifacts(step: "Lift")
}
"""
)
result = client.execute(q)
assert "produce_artifacts" in result, result["produce_artifacts"] != ""
def test_function_endpoint(client):
client.execute(gql("{ root { lift } }"))
q = gql(
"""
{
container(name: "module.ll", step: "Isolate") {
targets {
serialized
}
}
}
"""
)
result = client.execute(q)
first_function = next(t for t in result["container"]["targets"] if t["serialized"] != "")
q = gql(
"""
query function($param1: String!) {
function(param1: $param1) {
isolate
}
}
"""
)
result = client.execute(q, {"param1": first_function["serialized"]})
assert result["function"]["isolate"] is not None