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

Misc feature adds and changes #31

Merged
merged 1 commit into from
May 2, 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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ solana-test-suite run-tests --input-dir <input_dir> --solana-target <solana_targ
You can pick out a single test case and run it to view the instruction effects via output with the following command:

```sh
solana-test-suite execute-single-instruction --input <input_file> --target <shared_lib>
solana-test-suite exec-instr --input <input_file> --target <shared_lib>
```

| Argument | Description |
Expand All @@ -78,7 +78,7 @@ solana-test-suite execute-single-instruction --input <input_file> --target <shar
For failing test cases, it may be useful to analyze what could have differed between Solana and Firedancer. You can execute a Protobuf message (human-readable or binary) through the desired client as such:

```sh
solana-test-suite debug-instruction --input <input_file> --target <shared_lib> --debugger <gdb,rust-gdb,etc>
solana-test-suite debug-instr --input <input_file> --target <shared_lib> --debugger <gdb,rust-gdb,etc>
```

| Argument | Description |
Expand Down Expand Up @@ -111,7 +111,7 @@ solana-test-suite minimize-tests --input-dir <input_dir> --solana-target <solana
Create full test fixtures containing both instruction context and effects. Effects are computed by running instruction context through `--solana-target`. Fixtures with `None` values for instruction context/effects are not included.

```sh
solana-test-suite create-fixtures --input-dir <input_dir> --solana-target <solana_target.so> --output-dir <fixtures_output_dir> --num-processes <num_processes>
solana-test-suite create-fixtures --input-dir <input_dir> --solana-target <solana_target.so> --output-dir <fixtures_output_dir> --num-processes <num_processes> [--readable]
```

| Argument | Description |
Expand All @@ -120,6 +120,7 @@ solana-test-suite create-fixtures --input-dir <input_dir> --solana-target <solan
| `--solana-target` | Path to Solana Agave shared object (.so) target file |
| `--output-dir` | Instruction fixtures dumping directory |
| `--num-processes` | Number of processes to use |
| `--readable` | Output fixtures in human-readable format |


### Validation
Expand Down
31 changes: 28 additions & 3 deletions src/test_suite/fixture_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from test_suite.codec_utils import encode_input, encode_output
from test_suite.multiprocessing_utils import prune_execution_result
import test_suite.globals as globals
import test_suite.invoke_pb2 as pb
from google.protobuf import text_format


def create_fixture(
Expand Down Expand Up @@ -50,7 +52,8 @@ def create_fixture(

def write_fixture_to_disk(file_stem: str, serialized_instruction_fixture: str) -> int:
"""
Writes instruction fixtures to disk.
Writes instruction fixtures to disk. This function outputs in binary format unless
specified otherwise with the --readable flag.

Args:
- file_stem (str): File stem
Expand All @@ -62,7 +65,29 @@ def write_fixture_to_disk(file_stem: str, serialized_instruction_fixture: str) -
if serialized_instruction_fixture is None:
return 0

with open(f"{globals.output_dir}/{file_stem}.bin", "wb") as f:
f.write(serialized_instruction_fixture)
if globals.readable:
# Deserialize fixture
instr_fixture = pb.InstrFixture()
instr_fixture.ParseFromString(serialized_instruction_fixture)

# Encode fields for instruction context and effects
instr_context = pb.InstrContext()
instr_context.CopyFrom(instr_fixture.input)
encode_input(instr_context)

instr_effects = pb.InstrEffects()
instr_effects.CopyFrom(instr_fixture.output)
encode_output(instr_effects)

instr_fixture.input.CopyFrom(instr_context)
instr_fixture.output.CopyFrom(instr_effects)

with open(globals.output_dir / (file_stem + ".txt"), "w") as f:
f.write(
text_format.MessageToString(instr_fixture, print_unknown_fields=False)
)
else:
with open(f"{globals.output_dir}/{file_stem}.bin", "wb") as f:
f.write(serialized_instruction_fixture)

return 1
3 changes: 3 additions & 0 deletions src/test_suite/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@
# A FeaturePool object describing the hardcoded and supported features
# of the target
feature_pool = None

# (For fixtures) Whether to output in human-readable format
readable = False
2 changes: 1 addition & 1 deletion src/test_suite/minimize_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,6 @@ def minimize_single_test_case(test_file: Path) -> int:
del instruction_context.epoch_context.features.features[:]
instruction_context.epoch_context.features.features.extend(sorted(set(features)))

with open(globals.output_dir / test_file.name, "wb") as f:
with open(globals.output_dir / (test_file.stem + ".txt"), "wb") as f:
f.write(instruction_context.SerializeToString(deterministic=True))
return 1
4 changes: 2 additions & 2 deletions src/test_suite/multiprocessing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def process_instruction(

def generate_test_case(test_file: Path) -> tuple[str, str | None]:
"""
Reads in test files and generates a Protobuf object for a test case.
Reads in test files and generates an InstrContext Protobuf object for a test case.

Args:
- test_file (Path): Path to the file containing serialized instruction contexts.
Expand Down Expand Up @@ -121,7 +121,7 @@ def decode_single_test_case(test_file: Path) -> int:
instruction_context.ParseFromString(serialized_instruction_context)
encode_input(instruction_context)

with open(globals.output_dir / test_file.name, "w") as f:
with open(globals.output_dir / (test_file.stem + ".txt"), "w") as f:
f.write(
text_format.MessageToString(instruction_context, print_unknown_fields=False)
)
Expand Down
10 changes: 7 additions & 3 deletions src/test_suite/test_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@


@app.command()
def execute_single_instruction(
def exec_instr(
file: Path = typer.Option(None, "--input", "-i", help="Input file"),
shared_library: Path = typer.Option(
Path("impl/firedancer/build/native/clang/lib/libfd_exec_sol_compat.so"),
Expand Down Expand Up @@ -88,7 +88,7 @@ def execute_single_instruction(


@app.command()
def debug_instruction(
def debug_instr(
file: Path = typer.Option(None, "--input", "-i", help="Input file"),
shared_library: Path = typer.Option(
Path("impl/lib/libsolfuzz_firedancer.so"),
Expand Down Expand Up @@ -310,10 +310,14 @@ def create_fixtures(
num_processes: int = typer.Option(
4, "--num-processes", "-p", help="Number of processes to use"
),
readable: bool = typer.Option(
False, "--readable", "-r", help="Output fixtures in human-readable format"
),
):
# Specify globals
globals.output_dir = output_dir
globals.solana_shared_library = solana_shared_library
globals.readable = readable

# Create the output directory, if necessary
if globals.output_dir.exists():
Expand Down Expand Up @@ -474,7 +478,7 @@ def run_tests(
):
execution_results.append(result)

# Prune modified accounts that were not actually modified
# Prune accounts that were not actually modified
print("Pruning results...")
pruned_execution_results = []
with Pool(processes=num_processes) as pool:
Expand Down
Loading