@@ -64,6 +64,8 @@ defmodule Sentry.Test do
6464 @ spec maybe_collect ( Sentry.Event . t ( ) ) :: :collected | :not_collecting
6565 def maybe_collect ( % Sentry.Event { } = event ) do
6666 if Sentry.Config . test_mode? ( ) do
67+ ensure_ownership_server_started ( )
68+
6769 case NimbleOwnership . fetch_owner ( @ server , callers ( ) , @ key ) do
6870 { :ok , owner_pid } ->
6971 result =
@@ -102,24 +104,77 @@ defmodule Sentry.Test do
102104
103105 setup :start_collecting_sentry_reports
104106
107+ For a more flexible way to start collecting events, see `start_collecting/1`.
105108 """
106109 @ doc since: "10.2.0"
107110 @ spec start_collecting_sentry_reports ( map ( ) ) :: :ok
108111 def start_collecting_sentry_reports ( _context \\ % { } ) do
112+ start_collecting ( )
113+ end
114+
115+ @ doc """
116+ Starts collecting events.
117+
118+ This function starts collecting events reported from the given (*owner*) process. If you want to
119+ allow other processes to report events, you need to *allow* them to report events back
120+ to the owner process. See `allow/2` for more information on allowances. If the owner
121+ process is already *allowed by another process*, this function raises an error.
122+
123+ ## Options
124+
125+ * `:owner` - the PID of the owner process that will collect the events. Defaults to `self/0`.
126+
127+ * `:cleanup` - a boolean that controls whether collected resources around the owner process
128+ should be cleaned up when the owner process exits. Defaults to `true`. If `false`, you'll
129+ need to manually call `cleanup/1` to clean up the resources.
130+
131+ ## Examples
132+
133+ The `:cleanup` option can be used to implement expectation-based tests, akin to something
134+ like [`Mox.expect/4`](https://hexdocs.pm/mox/1.1.0/Mox.html#expect/4).
135+
136+ test "implementing an expectation-based test workflow" do
137+ test_pid = self()
138+
139+ Test.start_collecting(owner: test_pid, cleanup: false)
140+
141+ on_exit(fn ->
142+ assert [%Event{} = event] = Test.pop_sentry_reports(test_pid)
143+ assert event.message.formatted == "Oops"
144+ assert :ok = Test.cleanup(test_pid)
145+ end)
146+
147+ assert {:ok, ""} = Sentry.capture_message("Oops")
148+ end
149+
150+ """
151+ @ doc since: "10.2.0"
152+ @ spec start_collecting ( keyword ( ) ) :: :ok
153+ def start_collecting ( options \\ [ ] ) when is_list ( options ) do
154+ owner_pid = Keyword . get ( options , :owner , self ( ) )
155+ cleanup? = Keyword . get ( options , :cleanup , true )
156+
157+ callers =
158+ if owner_pid == self ( ) do
159+ callers ( )
160+ else
161+ [ owner_pid ]
162+ end
163+
109164 # Make sure the ownership server is started (this is idempotent).
110165 ensure_ownership_server_started ( )
111166
112- case NimbleOwnership . fetch_owner ( @ server , callers ( ) , @ key ) do
167+ case NimbleOwnership . fetch_owner ( @ server , callers , @ key ) do
113168 # No-op
114- { tag , owner_pid } when tag in [ :ok , :shared_owner ] and owner_pid == self ( ) ->
169+ { tag , ^ owner_pid } when tag in [ :ok , :shared_owner ] ->
115170 :ok
116171
117- { :shared_owner , _pid } ->
172+ { :shared_owner , _other_pid } ->
118173 raise ArgumentError ,
119174 "Sentry.Test is in global mode and is already collecting reported events"
120175
121- { :ok , another_pid } ->
122- raise ArgumentError , "already collecting reported events from #{ inspect ( another_pid ) } "
176+ { :ok , other_pid } ->
177+ raise ArgumentError , "already collecting reported events from #{ inspect ( other_pid ) } "
123178
124179 :error ->
125180 :ok
@@ -130,9 +185,25 @@ defmodule Sentry.Test do
130185 { :ignored , events || [ ] }
131186 end )
132187
188+ if not cleanup? do
189+ :ok = NimbleOwnership . set_owner_to_manual_cleanup ( @ server , owner_pid )
190+ end
191+
133192 :ok
134193 end
135194
195+ @ doc """
196+ Cleans up test resources associated with `owner_pid`.
197+
198+ See the `:cleanup` option in `start_collecting/1` and the corresponding
199+ example for more information.
200+ """
201+ @ doc since: "10.2.0"
202+ @ spec cleanup ( pid ( ) ) :: :ok
203+ def cleanup ( owner_pid ) when is_pid ( owner_pid ) do
204+ :ok = NimbleOwnership . cleanup_owner ( @ server , owner_pid )
205+ end
206+
136207 @ doc """
137208 Allows `pid_to_allow` to collect events back to the root process via `owner_pid`.
138209
@@ -180,17 +251,22 @@ defmodule Sentry.Test do
180251
181252 """
182253 @ doc since: "10.2.0"
183- @ spec pop_sentry_reports ( ) :: [ Sentry.Event . t ( ) ]
184- def pop_sentry_reports do
254+ @ spec pop_sentry_reports ( pid ( ) ) :: [ Sentry.Event . t ( ) ]
255+ def pop_sentry_reports ( owner_pid \\ self ( ) ) when is_pid ( owner_pid ) do
185256 result =
186- NimbleOwnership . get_and_update ( @ server , self ( ) , @ key , fn
187- nil -> { :not_collecting , [ ] }
188- events when is_list ( events ) -> { events , [ ] }
189- end )
257+ try do
258+ NimbleOwnership . get_and_update ( @ server , owner_pid , @ key , fn
259+ nil -> { :not_collecting , [ ] }
260+ events when is_list ( events ) -> { events , [ ] }
261+ end )
262+ catch
263+ :exit , { :noproc , _ } ->
264+ raise ArgumentError , "not collecting reported events from #{ inspect ( owner_pid ) } "
265+ end
190266
191267 case result do
192268 { :ok , :not_collecting } ->
193- raise ArgumentError , "not collecting reported events from #{ inspect ( self ( ) ) } "
269+ raise ArgumentError , "not collecting reported events from #{ inspect ( owner_pid ) } "
194270
195271 { :ok , events } ->
196272 events
@@ -203,7 +279,7 @@ defmodule Sentry.Test do
203279 ## Helpers
204280
205281 defp ensure_ownership_server_started do
206- case NimbleOwnership . start_link ( name: @ server ) do
282+ case Supervisor . start_child ( Sentry.Supervisor , NimbleOwnership . child_spec ( name: @ server ) ) do
207283 { :ok , pid } ->
208284 pid
209285
0 commit comments