1Code.require_file("../../test_helper.exs", __DIR__)
2
3defmodule Mix.Tasks.Profile.EprofTest do
4  use MixTest.Case
5
6  import ExUnit.CaptureIO
7  alias Mix.Tasks.Profile.Eprof
8
9  @expr "Enum.each(1..5, &String.Chars.Integer.to_string/1)"
10
11  test "profiles evaluated expression", context do
12    in_tmp(context.test, fn ->
13      assert capture_io(fn ->
14               Eprof.run(["-e", @expr])
15             end) =~ ~r/String\.Chars\.Integer\.to_string\/1\s+\d/
16    end)
17  end
18
19  test "profiles evaluated expression in multiple processes", context do
20    in_tmp(context.test, fn ->
21      assert capture_io(fn ->
22               Eprof.run(["-e", "spawn(fn -> #{@expr} end)"])
23             end) =~ ~r/String\.Chars\.Integer\.to_string\/1\s+\d/
24    end)
25  end
26
27  test "profiles the script", context do
28    in_tmp(context.test, fn ->
29      profile_script_name = "profile_script.ex"
30      File.write!(profile_script_name, @expr)
31
32      assert capture_io(fn ->
33               Eprof.run([profile_script_name])
34             end) =~ ~r/String\.Chars\.Integer\.to_string\/1\s+\d/
35    end)
36  end
37
38  test "filters based on count", context do
39    in_tmp(context.test, fn ->
40      refute capture_io(fn ->
41               Eprof.run(["--calls", "5", "-e", @expr])
42             end) =~ ":erlang.apply/2"
43    end)
44  end
45
46  test "sorts based on the calls count", context do
47    in_tmp(context.test, fn ->
48      assert capture_io(fn ->
49               Eprof.run(["--sort", "calls", "-e", @expr])
50             end) =~ ~r/erlang\.apply\/2.*String\.Chars\.Integer\.to_string\/1/s
51    end)
52  end
53
54  test "Module matching", context do
55    in_tmp(context.test, fn ->
56      refute capture_io(fn ->
57               Eprof.run(["--matching", "Enum", "-e", @expr])
58             end) =~ ~r/String\.Chars\.Integer\.to_string\/1/
59    end)
60  end
61
62  test "Module.function matching", context do
63    in_tmp(context.test, fn ->
64      refute capture_io(fn ->
65               Eprof.run(["--matching", "Enum.each", "-e", @expr])
66             end) =~ ~r/anonymous fn\/3 in Enum\.each\/2/
67    end)
68  end
69
70  test "Module.function/arity matching", context do
71    in_tmp(context.test, fn ->
72      assert capture_io(fn ->
73               Eprof.run(["--matching", "Enum.each/8", "-e", @expr])
74             end) =~ ~r/Profile done over 0 matching functions/
75    end)
76  end
77
78  test "errors on missing files", context do
79    in_tmp(context.test, fn ->
80      message = "No files matched pattern \"non-existent\" given to --require"
81
82      assert_raise Mix.Error, message, fn ->
83        capture_io(fn -> Eprof.run(["-r", "non-existent"]) end)
84      end
85
86      message = "No files matched pattern \"non-existent\" given to --require"
87
88      assert_raise Mix.Error, message, fn ->
89        capture_io(fn -> Eprof.run(["-pr", "non-existent"]) end)
90      end
91
92      assert_raise Mix.Error, "No such file: non-existent", fn ->
93        capture_io(fn -> Eprof.run(["non-existent"]) end)
94      end
95
96      File.mkdir_p!("lib")
97
98      assert_raise Mix.Error, "No such file: lib", fn ->
99        capture_io(fn -> Eprof.run(["lib"]) end)
100      end
101    end)
102  end
103
104  test "warmup", context do
105    in_tmp(context.test, fn ->
106      assert capture_io(fn ->
107               Eprof.run(["-e", @expr])
108             end) =~ "Warmup..."
109
110      refute capture_io(fn ->
111               Eprof.run(["-e", @expr, "--no-warmup"])
112             end) =~ "Warmup..."
113    end)
114  end
115end
116