defmodule Livebook.Runtime.Evaluator.ObjectTrackerTest do use ExUnit.Case, async: true alias Livebook.Runtime.Evaluator.ObjectTracker setup do {:ok, object_tracker} = start_supervised(ObjectTracker) %{object_tracker: object_tracker} end test "monitor/5 returns an error when the given object doesn't exist", %{object_tracker: object_tracker} do assert {:error, :bad_object} = ObjectTracker.monitor(object_tracker, :object1, self(), :object1_released, false) end test "sends scheduled monitor messages when all object references are released", %{object_tracker: object_tracker} do ObjectTracker.add_reference(object_tracker, :object1, {self(), :ref1}) ObjectTracker.add_reference(object_tracker, :object1, {self(), :ref2}) ObjectTracker.monitor(object_tracker, :object1, self(), :object1_released, false) ObjectTracker.remove_reference_sync(object_tracker, {self(), :ref1}) ObjectTracker.remove_reference_sync(object_tracker, {self(), :ref2}) assert_received :object1_released end test "does not execute hooks when other references still point to the object", %{object_tracker: object_tracker} do ObjectTracker.add_reference(object_tracker, :object1, {self(), :ref1}) ObjectTracker.add_reference(object_tracker, :object1, {self(), :ref2}) ObjectTracker.monitor(object_tracker, :object1, self(), :object1_released, false) ObjectTracker.remove_reference_sync(object_tracker, {self(), :ref1}) refute_received :object1_released end test "remove_reference_sync/2 awaits for monitor acknowledgments", %{object_tracker: object_tracker} do ObjectTracker.add_reference(object_tracker, :object1, {self(), :ref1}) ObjectTracker.monitor(object_tracker, :object1, self(), :object1_released1, true) ObjectTracker.monitor(object_tracker, :object1, self(), :object1_released2, false) myself = self() spawn_link(fn -> ObjectTracker.remove_reference_sync(object_tracker, {myself, :ref1}) send(myself, :removed) end) assert_receive {:object1_released1, ^object_tracker, reply_as} assert_receive :object1_released2 refute_receive :removed send(object_tracker, reply_as) assert_receive :removed end test "removes a reference if its process terminates", %{object_tracker: object_tracker} do reference_pid = spawn(fn -> receive do :stop -> :ok end end) ObjectTracker.add_reference(object_tracker, :object1, {reference_pid, :ref1}) ObjectTracker.monitor(object_tracker, :object1, self(), :object1_released, false) send(reference_pid, :stop) assert_receive :object1_released end end