v / vlib / v2 / transformer / inject_live_reload_to_flat_test.v
148 lines · 140 sloc · 4.75 KB · 3dd96de45339a469cb83ffacc0e50b017468e032
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4// vtest build: macos
5//
6// Bit-equality pin for the fifth flat-aware post_pass port (s159):
7// `inject_live_reload_to_flat` must produce a FlatAst whose signature
8// matches what the legacy `inject_live_reload` + `flatten_files` pair
9// produces. Uses all six FlatBuilder primitives needed by live_reload:
10// `replace_fn_body_stmts` (s158), `replace_file_stmt` (s155),
11// `prepend_file_stmts` (s156), plus the legacy `inject_live_into_stmts`
12// helper after decoding only the matching FnDecls from cursors.
13module transformer
14
15import v2.ast
16import v2.pref as vpref
17import v2.types
18
19// Local helper — `v test` compiles each `_test.v` file in isolation so the
20// shared transformer-test scaffold isn't visible here. Seeded with two
21// optional @[live] fn entries (controlled by `with_live`) and a stable
22// source_file path so both paths see identical LiveReloadParts.
23fn create_live_reload_to_flat_test_transformer(with_live bool) &Transformer {
24 env := &types.Environment{}
25 mut t := &Transformer{
26 pref: &vpref.Preferences{}
27 env: unsafe { env }
28 needed_clone_fns: map[string]string{}
29 needed_array_contains_fns: map[string]ArrayMethodInfo{}
30 needed_array_index_fns: map[string]ArrayMethodInfo{}
31 needed_array_last_index_fns: map[string]ArrayMethodInfo{}
32 local_decl_types: map[string]types.Type{}
33 live_source_file: '/tmp/_live_reload_to_flat_test.v'
34 }
35 if with_live {
36 t.live_fns << LiveFn{
37 decl_name: 'tick'
38 mangled_name: 'tick'
39 is_method: false
40 }
41 }
42 return t
43}
44
45fn make_main_file_with_main_and_for() ast.File {
46 return ast.File{
47 name: 'main_live.v'
48 mod: 'main'
49 stmts: [
50 ast.Stmt(ast.ModuleStmt{
51 name: 'main'
52 }),
53 ast.Stmt(ast.FnDecl{
54 name: 'tick'
55 stmts: [
56 ast.Stmt(ast.AssertStmt{
57 expr: ast.Expr(ast.BasicLiteral{
58 kind: .number
59 value: '1'
60 })
61 }),
62 ]
63 }),
64 ast.Stmt(ast.FnDecl{
65 name: 'main'
66 stmts: [
67 ast.Stmt(ast.ForStmt{
68 cond: ast.Expr(ast.BasicLiteral{
69 kind: .number
70 value: '1'
71 })
72 stmts: [
73 ast.Stmt(ast.ExprStmt{
74 expr: ast.Expr(ast.CallExpr{
75 lhs: ast.Expr(ast.Ident{
76 name: 'tick'
77 })
78 })
79 }),
80 ]
81 }),
82 ]
83 }),
84 ]
85 }
86}
87
88fn test_inject_live_reload_to_flat_empty_live_fns_is_noop() {
89 mut b := ast.new_flat_builder()
90 b.append_file(make_main_file_with_main_and_for())
91 baseline_sig := b.flat.signature()
92 mut t := create_live_reload_to_flat_test_transformer(false)
93 t.inject_live_reload_to_flat(mut b)
94 assert b.flat.signature() == baseline_sig
95}
96
97fn test_inject_live_reload_to_flat_no_main_fn_is_noop() {
98 // File with no main fn — locator returns none even though live_fns is
99 // populated; signature must be unchanged.
100 module_only_file := ast.File{
101 name: 'lib_live.v'
102 mod: 'main'
103 stmts: [
104 ast.Stmt(ast.ModuleStmt{
105 name: 'main'
106 }),
107 ast.Stmt(ast.FnDecl{
108 name: 'tick'
109 stmts: []ast.Stmt{}
110 }),
111 ]
112 }
113 mut b := ast.new_flat_builder()
114 b.append_file(module_only_file)
115 baseline_sig := b.flat.signature()
116 mut t := create_live_reload_to_flat_test_transformer(true)
117 t.inject_live_reload_to_flat(mut b)
118 assert b.flat.signature() == baseline_sig
119}
120
121fn test_inject_live_reload_to_flat_signature_matches_legacy() {
122 // Populated live_fns + main fn — splice happens. Compare the stmts-list
123 // subtree of the spliced file in both paths via `subtree_signature` on
124 // the file root's edge 2.
125 //
126 // Why subtree-of-stmts-list instead of full signature: the .file node's
127 // `extra` slot stores `intern(mod)` as a raw index. The legacy path
128 // flattens an already-mutated file (c_decls + global_decls + body
129 // emitted in declaration order → 'main' interned LATE) vs the flat
130 // path (file appended first → 'main' interned at slot 0 → splice
131 // re-emits the file root reusing saved mod_idx). Comparing the stmts
132 // list subtree skips the leaky `extra` field.
133 mut ref_files := [make_main_file_with_main_and_for()]
134 mut t_ref := create_live_reload_to_flat_test_transformer(true)
135 t_ref.inject_live_reload(mut ref_files)
136 ref_flat := ast.flatten_files(ref_files)
137 ref_stmts_list_id := ref_flat.child_at(ref_flat.files[0].file_id, 2)
138 ref_sub_sig := ref_flat.subtree_signature(ref_stmts_list_id)
139
140 mut b := ast.new_flat_builder()
141 b.append_file(make_main_file_with_main_and_for())
142 mut t_sub := create_live_reload_to_flat_test_transformer(true)
143 t_sub.inject_live_reload_to_flat(mut b)
144 sub_stmts_list_id := b.flat.child_at(b.flat.files[0].file_id, 2)
145 sub_sub_sig := b.flat.subtree_signature(sub_stmts_list_id)
146
147 assert ref_sub_sig == sub_sub_sig
148}
149