Fix S3 storage test

Fix the storage test by converting it from bash to python and also
testing all the occasions where a save should be triggered.
This commit is contained in:
Giacomo Vercesi
2025-03-26 15:01:04 +01:00
parent 6a88dc3d6a
commit 85998b1bd9
3 changed files with 127 additions and 34 deletions
@@ -11,4 +11,4 @@ commands:
- type: revng-qa.compiled
filter: example-executable-1 and with-debug-info
command: |-
"${SOURCES_ROOT}/share/revng/test/tests/storage.sh" "$INPUT" "$$(temp -d)"
"${SOURCES_ROOT}/share/revng/test/tests/s3_storage.py" "$INPUT" "$$(temp -d)"
+126
View File
@@ -0,0 +1,126 @@
#!/usr/bin/env python3
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
import atexit
import hashlib
import json
import socket
import sys
from pathlib import Path
from subprocess import DEVNULL, Popen
from uuid import uuid4
import yaml
from revng.internal.api import make_manager
def get_free_port():
"""Get an unbound port to spawn the S3 server on. Note that this is not
failproof and might fail since there's a time gap between when the script
is run and the server actually binds the socket"""
with socket.socket() as s:
s.bind(("127.0.0.1", 0))
return s.getsockname()[1]
def file_hash(path: str | Path):
with open(path, "rb") as f:
return hashlib.file_digest(f, "sha256").hexdigest()
def resolve_path(root: Path, subpath: str):
index_file = root / "index.yml._S3rver_object"
assert index_file.is_file()
with open(index_file) as f:
index_data = yaml.safe_load(f)
return root / f"{index_data[subpath]}._S3rver_object"
def main():
input_binary = sys.argv[1]
temporary_dir = sys.argv[2]
temporary_dir_path = Path(temporary_dir)
server_port = get_free_port()
# Run the server with the specified port
server_process = Popen(
[
"s3rver",
"--directory",
temporary_dir,
"--address",
"127.0.0.1",
"--port",
str(server_port),
"--configure-bucket",
"test",
],
stdout=DEVNULL,
stderr=DEVNULL,
)
atexit.register(server_process.kill)
workdir = f"s3://S3RVER:S3RVER@region+127.0.0.1:{server_port}/test/project-test-dir"
s3_root = temporary_dir_path / "test/project-test-dir"
manager = make_manager(workdir)
with open(input_binary, "rb") as f:
manager.set_input("input", f.read())
manager.run_analyses_list("revng-initial-auto-analysis").unwrap()
# Simple file check, this looks into the persistence directory of s3rver and
# checks that the input file and the file in 'begin/input' have the same
# contents. This is to guarantee that the S3 backend does not change the
# contents of the file.
s3_input_file_path = resolve_path(s3_root, "begin/input")
assert file_hash(input_binary) == file_hash(s3_input_file_path)
# Produce some stuff and then check that the file is actually saved
step_name = "disassemble"
container_name = "assembly.ptml.tar.gz"
targets = manager.get_targets(step_name, container_name)
manager.produce_target(step_name, [t.serialize() for t in targets], container_name)
assert manager.save()
s3_decompiled_path = resolve_path(s3_root, "disassemble/assembly.ptml.tar.gz")
assert s3_decompiled_path.is_file() and s3_decompiled_path.stat().st_size > 0
s3_decompiled_metadata = s3_decompiled_path.parent / (
s3_decompiled_path.name.removesuffix("._S3rver_object") + "._S3rver_metadata.json"
)
with open(s3_decompiled_metadata) as f:
metadata = json.load(f)
assert metadata["content-encoding"] == "gzip"
# Rename the first function in the model and check if it exists in the
# saved model
model = yaml.safe_load(manager.get_global("model.yml"))
first_function = model["Functions"][0]
unique_id = str(uuid4()).replace("-", "")
diff = {
"Path": f"/Functions/{first_function['Entry']}/CustomName",
"Add": unique_id,
"Remove": "",
}
if "CustomName" in first_function:
diff["Remove"] = first_function["CustomName"]
diff_content = yaml.safe_dump({"Changes": [diff]})
manager.run_analysis(
"initial",
"apply-diff",
{},
{"apply-diff-global-name": "model.yml", "apply-diff-diff-content": diff_content},
).unwrap()
model_path = resolve_path(s3_root, "context/model.yml")
assert unique_id in model_path.read_text()
# Also check that the partial save did not delete some file
assert s3_decompiled_path.is_file() and s3_decompiled_path.stat().st_size > 0
if __name__ == "__main__":
main()
-33
View File
@@ -1,33 +0,0 @@
#!/bin/bash
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
set -euo pipefail
# Python oneliner to get an unbound port to spawn the S3 server on. Note that
# this is not failproof and might fail since there's a time gap between when
# the script is run and the server actually binds the socket
S3_PORT=$(python -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()')
# Run the server with the specified port
s3rver --directory "$2" --address 127.0.0.1 --port "$S3_PORT" --configure-bucket test &>/dev/null &
# Save the server pid to kill it on exit
S3RVER_PID=$!
trap 'kill -9 $S3RVER_PID' EXIT
# Run revng with the server as the resume directory
revng artifact \
--resume="s3://S3RVER:S3RVER@region+127.0.0.1:$S3_PORT/test/project-test-dir" \
--analyze \
-o /dev/null \
disassemble "$1"
# Simple file check, this looks into the persistence directory of s3rver and
# checks that the input file and the file in 'begin/input' have the same
# contents. This is to guarantee that the S3 backend does not change the
# contents of the file.
INDEX_FILE="$2/test/project-test-dir/index.yml._S3rver_object"
test -f "$INDEX_FILE"
INPUT_ID="$(grep -Po '(?<=^begin/input:).*' "$INDEX_FILE" | tr -d ' ' | tr -d "'")"
INPUT_FILE="$2/test/project-test-dir/$INPUT_ID._S3rver_object"
sha256sum --quiet --check <(sha256sum <"$INPUT_FILE") <"$1"