Skip to content

Commit

Permalink
Merge pull request #31 from firedancer-io/mjain/improvements
Browse files Browse the repository at this point in the history
Misc feature adds and changes
  • Loading branch information
mjain-jump authored May 2, 2024
2 parents 231dacf + 493400e commit 99168fd
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 12 deletions.
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

0 comments on commit 99168fd

Please sign in to comment.