syntax-symphony 0.2.1__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {syntax_symphony-0.2.1/src/syntax_symphony.egg-info → syntax_symphony-0.3.0}/PKG-INFO +10 -4
  2. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/README.md +9 -3
  3. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/pyproject.toml +1 -1
  4. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony/cli.py +16 -1
  5. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony/fuzzer.py +6 -2
  6. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0/src/syntax_symphony.egg-info}/PKG-INFO +10 -4
  7. syntax_symphony-0.3.0/tests/test_fuzzer.py +230 -0
  8. syntax_symphony-0.2.1/tests/test_fuzzer.py +0 -21
  9. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/LICENSE +0 -0
  10. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/setup.cfg +0 -0
  11. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony/__init__.py +0 -0
  12. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony/derivation_tree.py +0 -0
  13. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony/grammar.py +0 -0
  14. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony/py.typed +0 -0
  15. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony.egg-info/SOURCES.txt +0 -0
  16. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony.egg-info/dependency_links.txt +0 -0
  17. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony.egg-info/entry_points.txt +0 -0
  18. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony.egg-info/requires.txt +0 -0
  19. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/src/syntax_symphony.egg-info/top_level.txt +0 -0
  20. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/tests/test_cli.py +0 -0
  21. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/tests/test_derivation_tree.py +0 -0
  22. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/tests/test_grammar.py +0 -0
  23. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/tests/test_public_api.py +0 -0
  24. {syntax_symphony-0.2.1 → syntax_symphony-0.3.0}/tests/test_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syntax_symphony
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Efficient grammar-based fuzzer.
5
5
  Author-email: Stanimir Iglev <iglev.stanimir@gmail.com>
6
6
  Maintainer-email: Stanimir Iglev <iglev.stanimir@gmail.com>
@@ -129,11 +129,14 @@ ssfuzz -g examples/expr_grammar.json -c 100 --start begin
129
129
 
130
130
  # Set the file extension
131
131
  ssfuzz -g examples/expr_grammar.json -c 100 -e json
132
+
133
+ # Reproduce the same output sequence across runs
134
+ ssfuzz -g examples/expr_grammar.json -c 100 --seed 42
132
135
  ```
133
136
 
134
137
  ### Full syntax:
135
138
  ```
136
- ssfuzz [-h] -g FILE [-s SYMBOL] -c NUMBER [-d DIR] [-e EXT] [--max-depth NUMBER] [--min-depth NUMBER] [-k NUMBER]
139
+ ssfuzz [-h] -g FILE [-s SYMBOL] -c NUMBER [-d DIR] [-e EXT] [--max-depth NUMBER] [--min-depth NUMBER] [-k NUMBER] [--seed NUMBER]
137
140
 
138
141
  Syntax Symphony Fuzzer
139
142
 
@@ -152,6 +155,7 @@ options:
152
155
  --min-depth NUMBER Minimum depth for the derivation trees. Default: 1
153
156
  -k NUMBER, --kcov NUMBER
154
157
  Number of strings to generate for k-cov. Default: 1
158
+ --seed NUMBER Random seed for reproducible fuzzing. Default: non-deterministic
155
159
  ```
156
160
 
157
161
  ## API
@@ -177,8 +181,8 @@ grammar = Grammar({
177
181
  "<number>": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
178
182
  })
179
183
 
180
- # Create the fuzzer
181
- fuzzer = SyntaxSymphony(grammar)
184
+ # Create the fuzzer (pass seed=42 for reproducible output)
185
+ fuzzer = SyntaxSymphony(grammar, seed=42)
182
186
 
183
187
  # Generate 10 test cases
184
188
  for i in range(10):
@@ -186,6 +190,8 @@ for i in range(10):
186
190
  print(test_case)
187
191
  ```
188
192
 
193
+ Pass `seed` to get the same sequence of outputs across runs. The fuzzer uses a private `random.Random` instance, so it does not affect global random state. Omit `seed` (or pass `None`) for non-deterministic fuzzing.
194
+
189
195
  ## Contributing
190
196
  We welcome contributions from the community. If you have ideas for improvements, new features, or bug fixes, please submit a pull request or open an issue on our [GitHub repository](https://github.com/StanimirIglev/syntax-symphony).
191
197
 
@@ -82,11 +82,14 @@ ssfuzz -g examples/expr_grammar.json -c 100 --start begin
82
82
 
83
83
  # Set the file extension
84
84
  ssfuzz -g examples/expr_grammar.json -c 100 -e json
85
+
86
+ # Reproduce the same output sequence across runs
87
+ ssfuzz -g examples/expr_grammar.json -c 100 --seed 42
85
88
  ```
86
89
 
87
90
  ### Full syntax:
88
91
  ```
89
- ssfuzz [-h] -g FILE [-s SYMBOL] -c NUMBER [-d DIR] [-e EXT] [--max-depth NUMBER] [--min-depth NUMBER] [-k NUMBER]
92
+ ssfuzz [-h] -g FILE [-s SYMBOL] -c NUMBER [-d DIR] [-e EXT] [--max-depth NUMBER] [--min-depth NUMBER] [-k NUMBER] [--seed NUMBER]
90
93
 
91
94
  Syntax Symphony Fuzzer
92
95
 
@@ -105,6 +108,7 @@ options:
105
108
  --min-depth NUMBER Minimum depth for the derivation trees. Default: 1
106
109
  -k NUMBER, --kcov NUMBER
107
110
  Number of strings to generate for k-cov. Default: 1
111
+ --seed NUMBER Random seed for reproducible fuzzing. Default: non-deterministic
108
112
  ```
109
113
 
110
114
  ## API
@@ -130,8 +134,8 @@ grammar = Grammar({
130
134
  "<number>": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
131
135
  })
132
136
 
133
- # Create the fuzzer
134
- fuzzer = SyntaxSymphony(grammar)
137
+ # Create the fuzzer (pass seed=42 for reproducible output)
138
+ fuzzer = SyntaxSymphony(grammar, seed=42)
135
139
 
136
140
  # Generate 10 test cases
137
141
  for i in range(10):
@@ -139,6 +143,8 @@ for i in range(10):
139
143
  print(test_case)
140
144
  ```
141
145
 
146
+ Pass `seed` to get the same sequence of outputs across runs. The fuzzer uses a private `random.Random` instance, so it does not affect global random state. Omit `seed` (or pass `None`) for non-deterministic fuzzing.
147
+
142
148
  ## Contributing
143
149
  We welcome contributions from the community. If you have ideas for improvements, new features, or bug fixes, please submit a pull request or open an issue on our [GitHub repository](https://github.com/StanimirIglev/syntax-symphony).
144
150
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "syntax_symphony"
7
- version = "0.2.1"
7
+ version = "0.3.0"
8
8
  description = "Efficient grammar-based fuzzer."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -85,6 +85,15 @@ def ssfuzz() -> None:
85
85
  type=int,
86
86
  help="Number of strings to generate for k-cov. Default: 1",
87
87
  )
88
+ parser.add_argument(
89
+ "--seed",
90
+ dest="seed",
91
+ metavar="NUMBER",
92
+ default=None,
93
+ required=False,
94
+ type=int,
95
+ help="Random seed for reproducible fuzzing. Default: non-deterministic",
96
+ )
88
97
 
89
98
  args = parser.parse_args()
90
99
 
@@ -113,7 +122,13 @@ def ssfuzz() -> None:
113
122
  print(f"Error: {exc}", file=sys.stderr)
114
123
  sys.exit(1)
115
124
 
116
- fuzzer = SyntaxSymphony(grammar, args.kcov, args.min_depth, args.max_depth)
125
+ fuzzer = SyntaxSymphony(
126
+ grammar,
127
+ args.kcov,
128
+ args.min_depth,
129
+ args.max_depth,
130
+ seed=args.seed,
131
+ )
117
132
 
118
133
  if not os.path.isdir(args.output_dir):
119
134
  os.makedirs(args.output_dir)
@@ -28,6 +28,7 @@ class SyntaxSymphony:
28
28
  kcov: int = 1,
29
29
  min_depth: int = 0,
30
30
  max_depth: int = 10,
31
+ seed: int | None = None,
31
32
  ):
32
33
  """Initialize a fuzzer.
33
34
 
@@ -38,12 +39,15 @@ class SyntaxSymphony:
38
39
  Defaults to 0.
39
40
  max_depth (int, optional): Maximal depth for derivation trees.
40
41
  Defaults to 10.
42
+ seed (int | None, optional): Random seed for reproducible fuzzing.
43
+ Defaults to None (non-deterministic).
41
44
  """
42
45
  self.grammar = grammar
43
46
  self.start_symbol = grammar.start_symbol
44
47
  self._kcov = kcov
45
48
  self._min_depth = min_depth
46
49
  self._max_depth = max_depth
50
+ self._rng = random.Random(seed)
47
51
  self.symbol_costs: dict[str, int | float] = {}
48
52
  _logger.info("Computing costs...")
49
53
  self.costs = self.compute_cost()
@@ -56,7 +60,7 @@ class SyntaxSymphony:
56
60
  self.uncovered_k_paths = copy.deepcopy(self.k_paths)
57
61
  # NOTE: Shuffle the paths, so that we only need to pop from the list.
58
62
  for symbol in self.uncovered_k_paths:
59
- random.shuffle(self.uncovered_k_paths[symbol])
63
+ self._rng.shuffle(self.uncovered_k_paths[symbol])
60
64
  # self.k_tree_generator = self.convert_paths_to_trees(self.k_paths)
61
65
 
62
66
  def symbol_cost(self, symbol: str, seen: set[str]) -> int | float:
@@ -352,7 +356,7 @@ class SyntaxSymphony:
352
356
  item.children = k_tree.children
353
357
  else:
354
358
  grammar = self._pick_grammar(depth)
355
- expansion = random.choice(grammar[item.symbol])
359
+ expansion = self._rng.choice(grammar[item.symbol])
356
360
  tree_expansion = [self.symbol_to_tree(t) for t in expansion]
357
361
  item.children = tree_expansion
358
362
  queue.extend((depth + 1, t) for t in tree_expansion)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syntax_symphony
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Efficient grammar-based fuzzer.
5
5
  Author-email: Stanimir Iglev <iglev.stanimir@gmail.com>
6
6
  Maintainer-email: Stanimir Iglev <iglev.stanimir@gmail.com>
@@ -129,11 +129,14 @@ ssfuzz -g examples/expr_grammar.json -c 100 --start begin
129
129
 
130
130
  # Set the file extension
131
131
  ssfuzz -g examples/expr_grammar.json -c 100 -e json
132
+
133
+ # Reproduce the same output sequence across runs
134
+ ssfuzz -g examples/expr_grammar.json -c 100 --seed 42
132
135
  ```
133
136
 
134
137
  ### Full syntax:
135
138
  ```
136
- ssfuzz [-h] -g FILE [-s SYMBOL] -c NUMBER [-d DIR] [-e EXT] [--max-depth NUMBER] [--min-depth NUMBER] [-k NUMBER]
139
+ ssfuzz [-h] -g FILE [-s SYMBOL] -c NUMBER [-d DIR] [-e EXT] [--max-depth NUMBER] [--min-depth NUMBER] [-k NUMBER] [--seed NUMBER]
137
140
 
138
141
  Syntax Symphony Fuzzer
139
142
 
@@ -152,6 +155,7 @@ options:
152
155
  --min-depth NUMBER Minimum depth for the derivation trees. Default: 1
153
156
  -k NUMBER, --kcov NUMBER
154
157
  Number of strings to generate for k-cov. Default: 1
158
+ --seed NUMBER Random seed for reproducible fuzzing. Default: non-deterministic
155
159
  ```
156
160
 
157
161
  ## API
@@ -177,8 +181,8 @@ grammar = Grammar({
177
181
  "<number>": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
178
182
  })
179
183
 
180
- # Create the fuzzer
181
- fuzzer = SyntaxSymphony(grammar)
184
+ # Create the fuzzer (pass seed=42 for reproducible output)
185
+ fuzzer = SyntaxSymphony(grammar, seed=42)
182
186
 
183
187
  # Generate 10 test cases
184
188
  for i in range(10):
@@ -186,6 +190,8 @@ for i in range(10):
186
190
  print(test_case)
187
191
  ```
188
192
 
193
+ Pass `seed` to get the same sequence of outputs across runs. The fuzzer uses a private `random.Random` instance, so it does not affect global random state. Omit `seed` (or pass `None`) for non-deterministic fuzzing.
194
+
189
195
  ## Contributing
190
196
  We welcome contributions from the community. If you have ideas for improvements, new features, or bug fixes, please submit a pull request or open an issue on our [GitHub repository](https://github.com/StanimirIglev/syntax-symphony).
191
197
 
@@ -0,0 +1,230 @@
1
+ import re
2
+
3
+ import pytest
4
+
5
+ from syntax_symphony.derivation_tree import DT
6
+ from syntax_symphony.fuzzer import SyntaxSymphony
7
+ from syntax_symphony.grammar import Grammar
8
+
9
+ # --- k-path computation ---
10
+
11
+
12
+ def test_compute_k_paths_k1(tiny_grammar: Grammar) -> None:
13
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=1)
14
+ paths = fuzzer._compute_k_paths(1)
15
+
16
+ assert paths["<start>"] == [[["<A>"]]]
17
+ assert paths["<A>"] == [[["<B>", "x"]], [["y"]]]
18
+ assert paths["<B>"] == [[["z"]]]
19
+
20
+
21
+ def test_compute_k_paths_k2(tiny_grammar: Grammar) -> None:
22
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=1)
23
+ paths = fuzzer._compute_k_paths(2)
24
+
25
+ assert paths["<A>"] == [[["<B>", "x"], ["z"]]]
26
+ assert paths["<start>"] == [[["<A>"], ["<B>", "x"]], [["<A>"], ["y"]]]
27
+ assert paths["<B>"] == []
28
+
29
+
30
+ def test_compute_k_paths_k3(tiny_grammar: Grammar) -> None:
31
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=1)
32
+ paths = fuzzer._compute_k_paths(3)
33
+
34
+ assert paths["<start>"] == [[["<A>"], ["<B>", "x"], ["z"]]]
35
+ assert paths["<A>"] == []
36
+ assert paths["<B>"] == []
37
+
38
+
39
+ def test_compute_k_paths_accumulates_through_kcov(tiny_grammar: Grammar) -> None:
40
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=2)
41
+
42
+ assert len(fuzzer.k_paths["<start>"]) == 3
43
+ assert len(fuzzer.k_paths["<A>"]) == 3
44
+ assert len(fuzzer.k_paths["<B>"]) == 1
45
+
46
+
47
+ def test_compute_k_paths_invalid_k(tiny_grammar: Grammar) -> None:
48
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=1)
49
+
50
+ with pytest.raises(ValueError, match="max_k must be at least 1"):
51
+ fuzzer.compute_k_paths(0)
52
+
53
+
54
+ def test_terminal_only_k_paths(terminal_only_grammar: Grammar) -> None:
55
+ fuzzer = SyntaxSymphony(terminal_only_grammar, kcov=1)
56
+
57
+ assert fuzzer.k_paths == {"<start>": [[["hello"]]]}
58
+ assert fuzzer.remaining_k_paths() == 1
59
+
60
+
61
+ # --- cost computation and biased grammars ---
62
+
63
+
64
+ def test_compute_cost(tiny_grammar: Grammar) -> None:
65
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=1)
66
+
67
+ assert fuzzer.costs["<A>"] == {"<B>x": 2, "y": 1}
68
+ assert fuzzer.costs["<B>"] == {"z": 1}
69
+ assert fuzzer.costs["<start>"] == {"<A>": 2}
70
+
71
+
72
+ def test_biased_grammar_min_max(tiny_grammar: Grammar) -> None:
73
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=1)
74
+
75
+ assert fuzzer.minimizing_grammar["<A>"] == [["y"]]
76
+ assert fuzzer.maximizing_grammar["<A>"] == [["<B>", "x"]]
77
+
78
+
79
+ # --- coverage tracking ---
80
+
81
+
82
+ def test_remaining_k_paths_initial_count(tiny_grammar: Grammar) -> None:
83
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=2, seed=42)
84
+
85
+ assert fuzzer.remaining_k_paths() == 7
86
+
87
+
88
+ def test_k_paths_depleted_after_fuzzing(tiny_grammar: Grammar) -> None:
89
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=2, seed=42)
90
+ initial = fuzzer.remaining_k_paths()
91
+
92
+ for _ in range(initial):
93
+ fuzzer.fuzz()
94
+
95
+ assert fuzzer.remaining_k_paths() == 0
96
+
97
+
98
+ def test_no_paths_reused_after_depletion(tiny_grammar: Grammar) -> None:
99
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=2, seed=42)
100
+
101
+ while fuzzer.remaining_k_paths() > 0:
102
+ fuzzer.fuzz()
103
+
104
+ for _ in range(5):
105
+ before = fuzzer.remaining_k_paths()
106
+ fuzzer.fuzz()
107
+ assert fuzzer.remaining_k_paths() == before == 0
108
+
109
+
110
+ # --- depth control ---
111
+
112
+
113
+ def test_max_depth_prefers_shallower_expansions(
114
+ depth_chain_grammar: Grammar,
115
+ ) -> None:
116
+ shallow = SyntaxSymphony(
117
+ depth_chain_grammar,
118
+ kcov=1,
119
+ min_depth=0,
120
+ max_depth=1,
121
+ seed=42,
122
+ )
123
+ deep = SyntaxSymphony(
124
+ depth_chain_grammar,
125
+ kcov=1,
126
+ min_depth=0,
127
+ max_depth=10,
128
+ seed=42,
129
+ )
130
+
131
+ shallow_tree = shallow.tree_fuzz(DT(shallow.start_symbol, None))
132
+ deep_tree = deep.tree_fuzz(DT(deep.start_symbol, None))
133
+
134
+ assert shallow_tree.height() < deep_tree.height()
135
+
136
+
137
+ def test_min_depth_biases_toward_deeper_expansions(
138
+ depth_chain_grammar: Grammar,
139
+ ) -> None:
140
+ low_min = SyntaxSymphony(
141
+ depth_chain_grammar,
142
+ kcov=1,
143
+ min_depth=0,
144
+ max_depth=10,
145
+ seed=7,
146
+ )
147
+ high_min = SyntaxSymphony(
148
+ depth_chain_grammar,
149
+ kcov=1,
150
+ min_depth=2,
151
+ max_depth=10,
152
+ seed=7,
153
+ )
154
+
155
+ low_tree = low_min.tree_fuzz(DT(low_min.start_symbol, None))
156
+ high_tree = high_min.tree_fuzz(DT(high_min.start_symbol, None))
157
+
158
+ assert high_tree.height() >= low_tree.height()
159
+
160
+
161
+ # --- seed reproducibility ---
162
+
163
+
164
+ def test_same_seed_same_output(tiny_grammar: Grammar) -> None:
165
+ first = SyntaxSymphony(tiny_grammar, kcov=2, seed=99)
166
+ second = SyntaxSymphony(tiny_grammar, kcov=2, seed=99)
167
+
168
+ assert [first.fuzz() for _ in range(5)] == [second.fuzz() for _ in range(5)]
169
+
170
+
171
+ def test_different_seed_different_output(tiny_grammar: Grammar) -> None:
172
+ first = SyntaxSymphony(tiny_grammar, kcov=2, seed=99)
173
+ second = SyntaxSymphony(tiny_grammar, kcov=2, seed=100)
174
+
175
+ assert [first.fuzz() for _ in range(5)] != [second.fuzz() for _ in range(5)]
176
+
177
+
178
+ def test_same_seed_same_shuffle_order(tiny_grammar: Grammar) -> None:
179
+ first = SyntaxSymphony(tiny_grammar, kcov=2, seed=42)
180
+ second = SyntaxSymphony(tiny_grammar, kcov=2, seed=42)
181
+
182
+ assert first.uncovered_k_paths == second.uncovered_k_paths
183
+
184
+
185
+ # --- helpers and edge cases ---
186
+
187
+
188
+ def test_symbol_to_tree(tiny_grammar: Grammar) -> None:
189
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=1)
190
+
191
+ nonterminal = fuzzer.symbol_to_tree("<A>")
192
+ terminal = fuzzer.symbol_to_tree("x")
193
+
194
+ assert nonterminal.symbol == "<A>"
195
+ assert nonterminal.children is None
196
+ assert terminal.symbol == "x"
197
+ assert terminal.children == []
198
+
199
+
200
+ def test_complete_tree(tiny_grammar: Grammar) -> None:
201
+ fuzzer = SyntaxSymphony(tiny_grammar, kcov=1, seed=42)
202
+ partial = DT("<start>", [DT("<A>", None)])
203
+ completed = fuzzer.complete_tree(partial)
204
+
205
+ assert completed.to_str()
206
+ assert "<" not in completed.to_str()
207
+
208
+
209
+ def test_fuzz_returns_only_terminals(expr_grammar: Grammar) -> None:
210
+ fuzzer = SyntaxSymphony(expr_grammar, kcov=1, seed=42)
211
+
212
+ for _ in range(10):
213
+ result = fuzzer.fuzz()
214
+ assert isinstance(result, str)
215
+ assert len(result) > 0
216
+ assert not re.search(r"<[^>]+>", result)
217
+
218
+
219
+ def test_recursive_grammar_fuzz_succeeds(expr_grammar: Grammar) -> None:
220
+ fuzzer = SyntaxSymphony(expr_grammar, kcov=2, seed=42)
221
+
222
+ results = {fuzzer.fuzz() for _ in range(20)}
223
+ assert len(results) > 1
224
+
225
+
226
+ def test_fuzz(expr_grammar: Grammar) -> None:
227
+ symphony = SyntaxSymphony(expr_grammar, seed=42)
228
+ result = symphony.fuzz()
229
+ assert isinstance(result, str)
230
+ assert len(result) > 0
@@ -1,21 +0,0 @@
1
- from syntax_symphony.fuzzer import SyntaxSymphony
2
- from syntax_symphony.grammar import Grammar
3
-
4
- grammar = Grammar(
5
- {
6
- "<start>": ["<expr>"],
7
- "<expr>": ["<term> + <expr>", "<term> - <expr>", "<term>"],
8
- "<term>": ["<factor> * <term>", "<factor> / <term>", "<factor>"],
9
- "<factor>": ["<number>", "( <expr> )"],
10
- "<number>": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
11
- }
12
- )
13
-
14
- # TODO: More tests!
15
-
16
-
17
- def test_fuzz():
18
- symphony = SyntaxSymphony(grammar)
19
- result = symphony.fuzz()
20
- assert isinstance(result, str)
21
- assert len(result) > 0
File without changes