1Code.require_file("../test_helper.exs", __DIR__)
2
3defmodule Kernel.TracersTest do
4  use ExUnit.Case
5
6  import Code, only: [compile_string: 1]
7
8  def trace(event, %Macro.Env{} = env) do
9    send(self(), {event, env})
10    :ok
11  end
12
13  setup_all do
14    Code.put_compiler_option(:tracers, [__MODULE__])
15    Code.put_compiler_option(:parser_options, columns: true)
16
17    on_exit(fn ->
18      Code.put_compiler_option(:tracers, [])
19      Code.put_compiler_option(:parser_options, [])
20    end)
21  end
22
23  test "traces start and stop" do
24    compile_string("""
25    Foo
26    """)
27
28    assert_receive {:start, %{lexical_tracker: pid}} when is_pid(pid)
29    assert_receive {:stop, %{lexical_tracker: pid}} when is_pid(pid)
30  end
31
32  test "traces alias references" do
33    compile_string("""
34    Foo
35    """)
36
37    assert_receive {{:alias_reference, meta, Foo}, _}
38    assert meta[:line] == 1
39    assert meta[:column] == 1
40  end
41
42  test "traces aliases" do
43    compile_string("""
44    alias Hello.World
45    World
46
47    alias Foo, as: Bar, warn: true
48    Bar
49    """)
50
51    assert_receive {{:alias, meta, Hello.World, World, []}, _}
52    assert meta[:line] == 1
53    assert meta[:column] == 1
54    assert_receive {{:alias_expansion, meta, World, Hello.World}, _}
55    assert meta[:line] == 2
56    assert meta[:column] == 1
57
58    assert_receive {{:alias, meta, Foo, Bar, [as: Bar, warn: true]}, _}
59    assert meta[:line] == 4
60    assert meta[:column] == 1
61    assert_receive {{:alias_expansion, meta, Bar, Foo}, _}
62    assert meta[:line] == 5
63    assert meta[:column] == 1
64  end
65
66  test "traces imports" do
67    compile_string("""
68    import Integer, only: [is_odd: 1, parse: 1]
69    true = is_odd(1)
70    {1, ""} = parse("1")
71    """)
72
73    assert_receive {{:import, meta, Integer, only: [is_odd: 1, parse: 1]}, _}
74    assert meta[:line] == 1
75    assert meta[:column] == 1
76
77    assert_receive {{:imported_macro, meta, Integer, :is_odd, 1}, _}
78    assert meta[:line] == 2
79    assert meta[:column] == 8
80
81    assert_receive {{:imported_function, meta, Integer, :parse, 1}, _}
82    assert meta[:line] == 3
83    assert meta[:column] == 11
84  end
85
86  test "traces structs" do
87    compile_string("""
88    %URI{path: "/"}
89    """)
90
91    assert_receive {{:struct_expansion, meta, URI, [:path]}, _}
92    assert meta[:line] == 1
93    assert meta[:column] == 1
94  end
95
96  test "traces remote" do
97    compile_string("""
98    require Integer
99    true = Integer.is_odd(1)
100    {1, ""} = Integer.parse("1")
101    """)
102
103    assert_receive {{:remote_macro, meta, Integer, :is_odd, 1}, _}
104    assert meta[:line] == 2
105    assert meta[:column] == 16
106
107    assert_receive {{:remote_function, meta, Integer, :parse, 1}, _}
108    assert meta[:line] == 3
109    assert meta[:column] == 19
110  end
111
112  test "traces remote via captures" do
113    compile_string("""
114    require Integer
115    &Integer.is_odd/1
116    &Integer.parse/1
117    """)
118
119    assert_receive {{:remote_macro, meta, Integer, :is_odd, 1}, _}
120    assert meta[:line] == 2
121    assert meta[:column] == 1
122
123    assert_receive {{:remote_function, meta, Integer, :parse, 1}, _}
124    assert meta[:line] == 3
125    assert meta[:column] == 1
126  end
127
128  test "traces locals" do
129    compile_string("""
130    defmodule Sample do
131      defmacro foo(arg), do: arg
132      def bar(arg), do: arg
133      def baz(arg), do: foo(arg) + bar(arg)
134    end
135    """)
136
137    assert_receive {{:local_macro, meta, :foo, 1}, _}
138    assert meta[:line] == 4
139    assert meta[:column] == 21
140
141    assert_receive {{:local_function, meta, :bar, 1}, _}
142    assert meta[:line] == 4
143    assert meta[:column] == 32
144  after
145    :code.purge(Sample)
146    :code.delete(Sample)
147  end
148
149  test "traces locals with capture" do
150    compile_string("""
151    defmodule Sample do
152      defmacro foo(arg), do: arg
153      def bar(arg), do: arg
154      def baz(_), do: {&foo/1, &bar/1}
155    end
156    """)
157
158    assert_receive {{:local_macro, meta, :foo, 1}, _}
159    assert meta[:line] == 4
160    assert meta[:column] == 20
161
162    assert_receive {{:local_function, meta, :bar, 1}, _}
163    assert meta[:line] == 4
164    assert meta[:column] == 28
165  after
166    :code.purge(Sample)
167    :code.delete(Sample)
168  end
169
170  test "traces modules" do
171    compile_string("""
172    defmodule Sample do
173      :ok
174    end
175    """)
176
177    assert_receive {{:on_module, <<_::binary>>, :none}, %{module: Sample, function: nil}}
178  after
179    :code.purge(Sample)
180    :code.delete(Sample)
181  end
182end
183