diff --git a/config/config.exs b/config/config.exs index 1a3d7222b..1d988326d 100644 --- a/config/config.exs +++ b/config/config.exs @@ -22,6 +22,16 @@ import Config # config :realtime_signs, + location_engine: Engine.Locations, + config_engine: Engine.Config, + headway_engine: Engine.ScheduledHeadways, + prediction_engine: Engine.Predictions, + alert_engine: Engine.Alerts, + last_trip_engine: Engine.LastTrip, + bridge_engine: Engine.ChelseaBridge, + route_engine: Engine.Routes, + bus_prediction_engine: Engine.BusPredictions, + sign_updater: PaEss.Updater, http_client: HTTPoison, posts_log_dir: "log/posts/", time_zone: "America/New_York", diff --git a/config/test.exs b/config/test.exs index 0b39095c5..6678402fd 100644 --- a/config/test.exs +++ b/config/test.exs @@ -1,6 +1,16 @@ import Config config :realtime_signs, + location_engine: Engine.Locations.Mock, + config_engine: Engine.Config.Mock, + headway_engine: Engine.ScheduledHeadways.Mock, + prediction_engine: Engine.Predictions.Mock, + alert_engine: Engine.Alerts.Mock, + last_trip_engine: Engine.LastTrip.Mock, + bridge_engine: Engine.ChelseaBridge.Mock, + route_engine: Engine.Routes.Mock, + bus_prediction_engine: Engine.BusPredictions.Mock, + sign_updater: PaEss.Updater.Mock, http_client: Fake.HTTPoison, trip_update_url: "https://fake_update/mbta-gtfs-s3/fake_trip_update.json", sign_head_end_host: "signs.example.com", diff --git a/lib/engine/alerts.ex b/lib/engine/alerts.ex index d47b590cf..624302258 100644 --- a/lib/engine/alerts.ex +++ b/lib/engine/alerts.ex @@ -1,5 +1,4 @@ defmodule Engine.Alerts do - @behaviour Engine.AlertsAPI use GenServer require Logger alias Engine.Alerts.Fetcher @@ -25,7 +24,7 @@ defmodule Engine.Alerts do GenServer.start_link(__MODULE__, opts, name: name) end - @impl true + @callback max_stop_status([Fetcher.stop_id()], [Fetcher.route_id()]) :: Fetcher.stop_status() def max_stop_status( tables \\ %{stops_table: @stops_table, routes_table: @routes_table}, stop_ids, @@ -51,7 +50,7 @@ defmodule Engine.Alerts do end end - @spec stop_status(:ets.tab(), Fetcher.stop_id()) :: Fetcher.stop_status() + @callback stop_status(Fetcher.stop_id()) :: Fetcher.stop_status() def stop_status(ets_table_name \\ @stops_table, stop_id) do case :ets.lookup(ets_table_name, stop_id) do [{^stop_id, status}] -> status @@ -59,7 +58,7 @@ defmodule Engine.Alerts do end end - @spec route_status(:ets.tab(), Fetcher.route_id()) :: Fetcher.stop_status() + @callback route_status(Fetcher.route_id()) :: Fetcher.stop_status() def route_status(ets_table_name \\ @routes_table, route_id) do case :ets.lookup(ets_table_name, route_id) do [{^route_id, status}] -> status diff --git a/lib/engine/alerts_api.ex b/lib/engine/alerts_api.ex deleted file mode 100644 index 9508c4502..000000000 --- a/lib/engine/alerts_api.ex +++ /dev/null @@ -1,4 +0,0 @@ -defmodule Engine.AlertsAPI do - alias Engine.Alerts.Fetcher - @callback max_stop_status([Fetcher.stop_id()], [Fetcher.route_id()]) :: Fetcher.stop_status() -end diff --git a/lib/engine/bus_predictions.ex b/lib/engine/bus_predictions.ex index b19ca0909..9d8046fd9 100644 --- a/lib/engine/bus_predictions.ex +++ b/lib/engine/bus_predictions.ex @@ -2,6 +2,7 @@ defmodule Engine.BusPredictions do use GenServer require Logger + @callback predictions_for_stop(String.t()) :: [Predictions.BusPrediction.t()] def predictions_for_stop(id) do case :ets.lookup(:bus_predictions, id) do [{^id, predictions}] -> predictions diff --git a/lib/engine/chelsea_bridge.ex b/lib/engine/chelsea_bridge.ex index c43e68ad7..d711eb939 100644 --- a/lib/engine/chelsea_bridge.ex +++ b/lib/engine/chelsea_bridge.ex @@ -2,6 +2,7 @@ defmodule Engine.ChelseaBridge do use GenServer require Logger + @callback bridge_status() :: %{raised: boolean() | nil, estimate: DateTime.t() | nil} def bridge_status() do case :ets.lookup(:bridge_status, :value) do [{:value, data}] -> data diff --git a/lib/engine/config.ex b/lib/engine/config.ex index 606c0c297..9b98f9ed4 100644 --- a/lib/engine/config.ex +++ b/lib/engine/config.ex @@ -2,8 +2,6 @@ defmodule Engine.Config do @moduledoc """ Manages the dynamic configurable pieces of the signs such as if they are on """ - @behaviour Engine.ConfigAPI - use GenServer require Logger alias Engine.Config.Headway @@ -31,7 +29,8 @@ defmodule Engine.Config do GenServer.start_link(__MODULE__, [], name: __MODULE__) end - @impl true + @callback sign_config(id :: String.t(), default :: Engine.Config.sign_config()) :: + Engine.Config.sign_config() def sign_config(table \\ @table_signs, sign_id, default) do case :ets.lookup(table, sign_id) do [{^sign_id, config}] when not is_nil(config) -> config @@ -39,13 +38,13 @@ defmodule Engine.Config do end end - @impl true + @callback headway_config(String.t(), DateTime.t()) :: Engine.Config.Headway.t() | nil def headway_config(table_name \\ @table_headways, headway_group, current_time) do time_period = Headway.current_time_period(current_time) Headways.get_headway(table_name, {headway_group, time_period}) end - @impl true + @callback scu_migrated?(String.t()) :: boolean() def scu_migrated?(table_name \\ @table_scus_migrated, scu_id) do case :ets.lookup(table_name, scu_id) do [{^scu_id, value}] -> value @@ -53,7 +52,7 @@ defmodule Engine.Config do end end - @spec chelsea_bridge_config(:ets.tab()) :: :off | :auto + @callback chelsea_bridge_config() :: :off | :auto def chelsea_bridge_config(table_name \\ @table_chelsea_bridge) do case :ets.lookup(table_name, :status) do [{_, "auto"}] -> :auto diff --git a/lib/engine/config_api.ex b/lib/engine/config_api.ex deleted file mode 100644 index 452afb1ad..000000000 --- a/lib/engine/config_api.ex +++ /dev/null @@ -1,6 +0,0 @@ -defmodule Engine.ConfigAPI do - @callback sign_config(id :: String.t(), default :: Engine.Config.sign_config()) :: - Engine.Config.sign_config() - @callback headway_config(String.t(), DateTime.t()) :: Engine.Config.Headway.t() | nil - @callback scu_migrated?(String.t()) :: boolean() -end diff --git a/lib/engine/last_trip.ex b/lib/engine/last_trip.ex index 7d9b37465..ebfb7e498 100644 --- a/lib/engine/last_trip.ex +++ b/lib/engine/last_trip.ex @@ -1,5 +1,4 @@ defmodule Engine.LastTrip do - @behaviour Engine.LastTripAPI use GenServer require Logger @@ -16,7 +15,7 @@ defmodule Engine.LastTrip do GenServer.start_link(__MODULE__, [], name: __MODULE__) end - @impl true + @callback get_recent_departures(String.t()) :: map() def get_recent_departures(recent_departures_table \\ @recent_departures_table, stop_id) do case :ets.lookup(recent_departures_table, stop_id) do [{^stop_id, departures}] -> departures @@ -24,7 +23,7 @@ defmodule Engine.LastTrip do end end - @impl true + @callback is_last_trip?(String.t()) :: boolean() def is_last_trip?(last_trips_table \\ @last_trips_table, trip_id) do case :ets.lookup(last_trips_table, trip_id) do [{^trip_id, _timestamp}] -> true diff --git a/lib/engine/last_trip_api.ex b/lib/engine/last_trip_api.ex deleted file mode 100644 index 92c3b6674..000000000 --- a/lib/engine/last_trip_api.ex +++ /dev/null @@ -1,4 +0,0 @@ -defmodule Engine.LastTripAPI do - @callback get_recent_departures(String.t()) :: map() - @callback is_last_trip?(String.t()) :: boolean() -end diff --git a/lib/engine/locations.ex b/lib/engine/locations.ex index f20940705..6c66481e1 100644 --- a/lib/engine/locations.ex +++ b/lib/engine/locations.ex @@ -3,8 +3,6 @@ defmodule Engine.Locations do Maintains an up-to-date internal state of the realtime locations of vehicles in the system. Fetches from the GTFS-rt enhanced JSON file about once per second. """ - @behaviour Engine.LocationsAPI - use GenServer require Logger alias Signs.Utilities.EtsUtils @@ -22,7 +20,7 @@ defmodule Engine.Locations do GenServer.start_link(__MODULE__, [], name: __MODULE__) end - @impl true + @callback for_vehicle(String.t()) :: Locations.Location.t() def for_vehicle(locations_table_id \\ @vehicle_locations_table, vehicle_id) do case :ets.lookup(locations_table_id, vehicle_id) do [{_, :none}] -> nil @@ -31,7 +29,7 @@ defmodule Engine.Locations do end end - @impl true + @callback for_stop(String.t()) :: [Locations.Location.t()] def for_stop(stop_id) do case :ets.lookup(@stop_locations_table, stop_id) do [{^stop_id, locations}] -> locations diff --git a/lib/engine/locations_api.ex b/lib/engine/locations_api.ex deleted file mode 100644 index 5c61257a2..000000000 --- a/lib/engine/locations_api.ex +++ /dev/null @@ -1,4 +0,0 @@ -defmodule Engine.LocationsAPI do - @callback for_vehicle(String.t()) :: Locations.Location.t() - @callback for_stop(String.t()) :: [Locations.Location.t()] -end diff --git a/lib/engine/predictions.ex b/lib/engine/predictions.ex index c98aa3725..d7202b886 100644 --- a/lib/engine/predictions.ex +++ b/lib/engine/predictions.ex @@ -7,8 +7,6 @@ defmodule Engine.Predictions do Offers a `for_stop/1` public interface to get a list of Predictions.Prediction's for a given GTFS stop. """ - @behaviour Engine.PredictionsAPI - use GenServer require Logger alias Signs.Utilities.EtsUtils @@ -23,7 +21,7 @@ defmodule Engine.Predictions do end @doc "The upcoming predicted times a vehicle will be at this stop" - @impl true + @callback for_stop(String.t(), 0 | 1) :: [Predictions.Prediction.t()] def for_stop(predictions_table_id \\ :trip_updates, gtfs_stop_id, direction_id) do case :ets.lookup(predictions_table_id, {gtfs_stop_id, direction_id}) do [{{^gtfs_stop_id, ^direction_id}, predictions}] -> predictions @@ -31,7 +29,7 @@ defmodule Engine.Predictions do end end - @impl true + @callback revenue_vehicles() :: MapSet.t() def revenue_vehicles() do case :ets.lookup(:revenue_vehicles, :all) do [{:all, data}] -> data diff --git a/lib/engine/predictions_api.ex b/lib/engine/predictions_api.ex deleted file mode 100644 index fcb8bd019..000000000 --- a/lib/engine/predictions_api.ex +++ /dev/null @@ -1,4 +0,0 @@ -defmodule Engine.PredictionsAPI do - @callback for_stop(String.t(), 0 | 1) :: [Predictions.Prediction.t()] - @callback revenue_vehicles() :: MapSet.t() -end diff --git a/lib/engine/routes.ex b/lib/engine/routes.ex index c02436716..7800e7f6f 100644 --- a/lib/engine/routes.ex +++ b/lib/engine/routes.ex @@ -2,7 +2,7 @@ defmodule Engine.Routes do use GenServer require Logger - @spec route_destination(String.t(), 0 | 1) :: String.t() | nil + @callback route_destination(String.t(), 0 | 1) :: String.t() | nil def route_destination(route_id, direction_id) do case :ets.lookup(:route_destinations, {route_id, direction_id}) do [{{^route_id, ^direction_id}, destination}] -> destination diff --git a/lib/engine/scheduled_headways.ex b/lib/engine/scheduled_headways.ex index eec354ec9..be137aa1c 100644 --- a/lib/engine/scheduled_headways.ex +++ b/lib/engine/scheduled_headways.ex @@ -4,7 +4,6 @@ defmodule Engine.ScheduledHeadways do Initially we will quickly update any newly registered stop so that we have something to show, then over time we will update every stop once every hour to make sure we stay up to date. """ - @behaviour Engine.ScheduledHeadwaysAPI use GenServer require Logger require Signs.Utilities.SignsConfig @@ -79,7 +78,7 @@ defmodule Engine.ScheduledHeadways do :ets.select(table_name, pattern) end - @impl true + @callback get_first_scheduled_departure([binary]) :: nil | DateTime.t() def get_first_scheduled_departure(stop_ids) do get_first_last_departures(stop_ids) |> Enum.map(&elem(&1, 0)) @@ -89,7 +88,7 @@ defmodule Engine.ScheduledHeadways do @doc "Checks if the given time is after the first scheduled stop and before the last. A buffer of minutes (positive) is subtracted from the first time. so that headways are shown for a short time before the first train." - @impl true + @callback display_headways?([String.t()], DateTime.t(), Engine.Config.Headway.t()) :: boolean() def display_headways?( table \\ :scheduled_headways_first_last_departures, stop_ids, diff --git a/lib/engine/scheduled_headways_api.ex b/lib/engine/scheduled_headways_api.ex deleted file mode 100644 index b81fe8f89..000000000 --- a/lib/engine/scheduled_headways_api.ex +++ /dev/null @@ -1,4 +0,0 @@ -defmodule Engine.ScheduledHeadwaysAPI do - @callback display_headways?([String.t()], DateTime.t(), Engine.Config.Headway.t()) :: boolean() - @callback get_first_scheduled_departure([binary]) :: nil | DateTime.t() -end diff --git a/lib/headway_analysis/server.ex b/lib/headway_analysis/server.ex index 564e057a5..ea5d98738 100644 --- a/lib/headway_analysis/server.ex +++ b/lib/headway_analysis/server.ex @@ -11,10 +11,7 @@ defmodule HeadwayAnalysis.Server do :sign_id, :headway_group, :stop_ids, - :vehicles_present, - :prediction_engine, - :config_engine, - :location_engine + :vehicles_present ] defstruct @enforce_keys @@ -31,10 +28,7 @@ defmodule HeadwayAnalysis.Server do sign_id: config["id"], headway_group: config["source_config"]["headway_group"], stop_ids: Enum.map(config["source_config"]["sources"], & &1["stop_id"]), - vehicles_present: MapSet.new(), - prediction_engine: Engine.Predictions, - config_engine: Engine.Config, - location_engine: Engine.Locations + vehicles_present: MapSet.new() }} end @@ -46,16 +40,16 @@ defmodule HeadwayAnalysis.Server do DateTime.utc_now() |> DateTime.shift_zone!(Application.get_env(:realtime_signs, :time_zone)) {headway_low, headway_high} = - case state.config_engine.headway_config(state.headway_group, current_time) do + case RealtimeSigns.config_engine().headway_config(state.headway_group, current_time) do %Engine.Config.Headway{range_low: low, range_high: high} -> {low, high} nil -> {nil, nil} end - revenue_vehicles = state.prediction_engine.revenue_vehicles() + revenue_vehicles = RealtimeSigns.prediction_engine().revenue_vehicles() new_vehicles_present = for stop_id <- state.stop_ids, - location <- state.location_engine.for_stop(stop_id), + location <- RealtimeSigns.location_engine().for_stop(stop_id), location.status == :stopped_at, into: MapSet.new() do location.vehicle_id diff --git a/lib/pa_ess/updater.ex b/lib/pa_ess/updater.ex index c3f6bec1c..4990797fa 100644 --- a/lib/pa_ess/updater.ex +++ b/lib/pa_ess/updater.ex @@ -1,23 +1,24 @@ defmodule PaEss.Updater do - @behaviour PaEss.UpdaterAPI - require Logger - @impl true + @callback set_background_message( + Signs.Realtime.t() | Signs.Bus.t(), + Content.Message.value(), + Content.Message.value() + ) :: :ok def set_background_message( %{ id: id, scu_id: scu_id, pa_ess_loc: pa_ess_loc, text_zone: text_zone, - default_mode: default_mode, - config_engine: config_engine + default_mode: default_mode }, top, bottom ) do log_config = - case config_engine.sign_config(id, default_mode) do + case RealtimeSigns.config_engine().sign_config(id, default_mode) do mode when is_atom(mode) -> mode mode when is_tuple(mode) -> elem(mode, 0) _ -> nil @@ -25,7 +26,7 @@ defmodule PaEss.Updater do visual = zip_pages(top, bottom) |> format_pages() tag = create_tag() - scu_migrated? = config_engine.scu_migrated?(scu_id) + scu_migrated? = RealtimeSigns.config_engine().scu_migrated?(scu_id) log_meta = [ sign_id: id, @@ -51,21 +52,26 @@ defmodule PaEss.Updater do end end - @impl true + @callback play_message( + Signs.Realtime.t() | Signs.Bus.t(), + [Content.Audio.value()], + [Content.Audio.tts_value()], + [keyword()] + ) :: + :ok def play_message( %{ id: id, scu_id: scu_id, pa_ess_loc: pa_ess_loc, - audio_zones: audio_zones, - config_engine: config_engine + audio_zones: audio_zones }, audios, tts_audios, log_metas ) do tags = Enum.map(audios, fn _ -> create_tag() end) - scu_migrated? = config_engine.scu_migrated?(scu_id) + scu_migrated? = RealtimeSigns.config_engine().scu_migrated?(scu_id) log_metas = Enum.zip([tts_audios, tags, log_metas]) diff --git a/lib/pa_ess/updater_api.ex b/lib/pa_ess/updater_api.ex deleted file mode 100644 index c5bbad610..000000000 --- a/lib/pa_ess/updater_api.ex +++ /dev/null @@ -1,15 +0,0 @@ -defmodule PaEss.UpdaterAPI do - @callback set_background_message( - Signs.Realtime.t() | Signs.Bus.t(), - Content.Message.value(), - Content.Message.value() - ) :: :ok - - @callback play_message( - Signs.Realtime.t() | Signs.Bus.t(), - [Content.Audio.value()], - [Content.Audio.tts_value()], - [keyword()] - ) :: - :ok -end diff --git a/lib/pa_ess/utilities.ex b/lib/pa_ess/utilities.ex index 1d9dfa9e9..af0f2af06 100644 --- a/lib/pa_ess/utilities.ex +++ b/lib/pa_ess/utilities.ex @@ -807,9 +807,9 @@ defmodule PaEss.Utilities do |> Enum.map(fn [top, bottom] -> {top, bottom, 3} end) end - @spec prediction_new_cars?(Predictions.Prediction.t(), Signs.Realtime.t()) :: boolean() - def prediction_new_cars?(prediction, sign) do - case sign.location_engine.for_vehicle(prediction.vehicle_id) do + @spec prediction_new_cars?(Predictions.Prediction.t()) :: boolean() + def prediction_new_cars?(prediction) do + case RealtimeSigns.location_engine().for_vehicle(prediction.vehicle_id) do %Locations.Location{route_id: "Red", multi_carriage_details: carriage_details} -> Enum.any?(carriage_details, fn carriage -> # See http://roster.transithistory.org/ for numbers of new cars diff --git a/lib/predictions/last_trip.ex b/lib/predictions/last_trip.ex index d9adf529f..7381baa83 100644 --- a/lib/predictions/last_trip.ex +++ b/lib/predictions/last_trip.ex @@ -23,7 +23,7 @@ defmodule Predictions.LastTrip do for {trip_id, predictions, vehicle_id} <- predictions_by_trip, prediction <- predictions do - vehicle_location = Engine.Locations.for_vehicle(vehicle_id) + vehicle_location = RealtimeSigns.location_engine().for_vehicle(vehicle_id) if vehicle_location && (vehicle_location.stop_id == prediction["stop_id"] and diff --git a/lib/predictions/predictions.ex b/lib/predictions/predictions.ex index ca3f7ae98..568d7bc53 100644 --- a/lib/predictions/predictions.ex +++ b/lib/predictions/predictions.ex @@ -42,7 +42,7 @@ defmodule Predictions.Predictions do stop_time_update, get_destination_stop_id(trip_update), vehicle_id, - Engine.Locations.for_vehicle(vehicle_id), + RealtimeSigns.location_engine().for_vehicle(vehicle_id), trip_update["trip"]["route_id"], trip_update["trip"]["direction_id"], trip_update["trip"]["trip_id"], diff --git a/lib/realtime_signs.ex b/lib/realtime_signs.ex index 3b81fde1e..e98853fb6 100644 --- a/lib/realtime_signs.ex +++ b/lib/realtime_signs.ex @@ -82,4 +82,15 @@ defmodule RealtimeSigns do Supervisor.child_spec({PaEss.HttpUpdater, i}, id: {PaEss.HttpUpdater, i}) end end + + def location_engine, do: Application.fetch_env!(:realtime_signs, :location_engine) + def config_engine, do: Application.fetch_env!(:realtime_signs, :config_engine) + def headway_engine, do: Application.fetch_env!(:realtime_signs, :headway_engine) + def prediction_engine, do: Application.fetch_env!(:realtime_signs, :prediction_engine) + def alert_engine, do: Application.fetch_env!(:realtime_signs, :alert_engine) + def last_trip_engine, do: Application.fetch_env!(:realtime_signs, :last_trip_engine) + def bridge_engine, do: Application.fetch_env!(:realtime_signs, :bridge_engine) + def route_engine, do: Application.fetch_env!(:realtime_signs, :route_engine) + def bus_prediction_engine, do: Application.fetch_env!(:realtime_signs, :bus_prediction_engine) + def sign_updater, do: Application.fetch_env!(:realtime_signs, :sign_updater) end diff --git a/lib/signs/bus.ex b/lib/signs/bus.ex index a457b7604..493309e1b 100644 --- a/lib/signs/bus.ex +++ b/lib/signs/bus.ex @@ -25,12 +25,6 @@ defmodule Signs.Bus do :chelsea_bridge, :read_loop_interval, :read_loop_offset, - :config_engine, - :prediction_engine, - :bridge_engine, - :alerts_engine, - :routes_engine, - :sign_updater, :prev_predictions, :prev_bridge_status, :current_messages, @@ -55,12 +49,6 @@ defmodule Signs.Bus do chelsea_bridge: String.t() | nil, read_loop_interval: integer(), read_loop_offset: integer(), - config_engine: module(), - prediction_engine: module(), - bridge_engine: module(), - alerts_engine: module(), - routes_engine: module(), - sign_updater: module(), prev_predictions: list(), prev_bridge_status: nil | map(), current_messages: tuple(), @@ -84,12 +72,6 @@ defmodule Signs.Bus do chelsea_bridge: sign["chelsea_bridge"], read_loop_interval: Map.fetch!(sign, "read_loop_interval"), read_loop_offset: Map.fetch!(sign, "read_loop_offset"), - config_engine: Engine.Config, - prediction_engine: Engine.BusPredictions, - bridge_engine: Engine.ChelseaBridge, - alerts_engine: Engine.Alerts, - routes_engine: Engine.Routes, - sign_updater: PaEss.Updater, prev_predictions: [], prev_bridge_status: nil, current_messages: {nil, nil}, @@ -144,28 +126,24 @@ defmodule Signs.Bus do id: id, default_mode: default_mode, configs: configs, - config_engine: config_engine, - prediction_engine: prediction_engine, - bridge_engine: bridge_engine, - alerts_engine: alerts_engine, prev_predictions: prev_predictions } = state # Fetch the data we need to compute the updated sign content. - config = config_engine.sign_config(id, default_mode) - bridge_enabled? = config_engine.chelsea_bridge_config() == :auto - bridge_status = bridge_engine.bridge_status() + config = RealtimeSigns.config_engine().sign_config(id, default_mode) + bridge_enabled? = RealtimeSigns.config_engine().chelsea_bridge_config() == :auto + bridge_status = RealtimeSigns.bridge_engine().bridge_status() current_time = Timex.now() all_route_ids = all_route_ids(state) route_alerts_lookup = for route_id <- all_route_ids, into: %{} do - {route_id, alerts_engine.route_status(route_id)} + {route_id, RealtimeSigns.alert_engine().route_status(route_id)} end stop_alerts_lookup = for stop_id <- all_stop_ids(state), into: %{} do - {stop_id, alerts_engine.stop_status(stop_id)} + {stop_id, RealtimeSigns.alert_engine().stop_status(stop_id)} end prev_predictions_lookup = @@ -202,7 +180,7 @@ defmodule Signs.Bus do # they are predicted there, let people on the lower busway know. id == "bus.Harvard_lower" && Enum.any?( - prediction_engine.predictions_for_stop("20761"), + RealtimeSigns.bus_prediction_engine().predictions_for_stop("20761"), &(&1.route_id in ["71", "73"]) ) -> special_harvard_content() @@ -234,7 +212,7 @@ defmodule Signs.Bus do state |> then(fn state -> if should_update?({top, bottom}, current_time, state) do - state.sign_updater.set_background_message(state, top, bottom) + RealtimeSigns.sign_updater().set_background_message(state, top, bottom) %{state | current_messages: {top, bottom}, last_update: current_time} else state @@ -268,10 +246,8 @@ defmodule Signs.Bus do end defp fetch_predictions(prev_predictions_lookup, current_time, state) do - %{prediction_engine: prediction_engine} = state - for stop_id <- all_stop_ids(state), - prediction <- prediction_engine.predictions_for_stop(stop_id), + prediction <- RealtimeSigns.bus_prediction_engine().predictions_for_stop(stop_id), prediction.departure_time do prev = prev_predictions_lookup[prediction_key(prediction)] @@ -387,14 +363,14 @@ defmodule Signs.Bus do messages = case content do [single] -> - format_long_message(single, current_time, state) + format_long_message(single, current_time) list -> Stream.chunk_every(list, 2, 2, [nil]) |> Stream.map(fn [first, second] -> [ - format_message(first, second, current_time, state), - format_message(second, first, current_time, state) + format_message(first, second, current_time), + format_message(second, first, current_time) ] end) end @@ -413,10 +389,10 @@ defmodule Signs.Bus do audios = case audio_content do [single] -> - long_message_audio(single, current_time, state) + long_message_audio(single, current_time) list -> - Enum.map(list, &message_audio(&1, current_time, state)) + Enum.map(list, &message_audio(&1, current_time)) |> add_preamble() end |> Stream.intersperse([:","]) @@ -430,10 +406,10 @@ defmodule Signs.Bus do [] [single] -> - [long_message_tts_audio(single, current_time, state)] + [long_message_tts_audio(single, current_time)] list -> - Enum.map(list, &message_tts_audio(&1, current_time, state)) + Enum.map(list, &message_tts_audio(&1, current_time)) |> Enum.join(" ") |> add_tts_preamble() |> List.wrap() @@ -470,12 +446,12 @@ defmodule Signs.Bus do else messages = for content <- [top_content, bottom_content] do - Enum.map(content, &format_message(&1, nil, current_time, state)) + Enum.map(content, &format_message(&1, nil, current_time)) |> paginate_message() end audios = - Enum.map(top_content ++ bottom_content, &message_audio(&1, current_time, state)) + Enum.map(top_content ++ bottom_content, &message_audio(&1, current_time)) |> add_preamble() |> Stream.intersperse([:","]) |> Stream.concat() @@ -488,7 +464,7 @@ defmodule Signs.Bus do [] list -> - Enum.map(list, &message_tts_audio(&1, current_time, state)) + Enum.map(list, &message_tts_audio(&1, current_time)) |> Enum.join(" ") |> add_tts_preamble() |> List.wrap() @@ -713,9 +689,9 @@ defmodule Signs.Bus do end end - defp format_message(nil, _, _, _state), do: "" + defp format_message(nil, _, _), do: "" - defp format_message({:predictions, [first | _]}, other, current_time, _state) do + defp format_message({:predictions, [first | _]}, other, current_time) do other_prediction = case other do {:predictions, [other_first | _]} -> other_first @@ -725,7 +701,7 @@ defmodule Signs.Bus do format_prediction(first, other_prediction, current_time) end - defp format_message({:alert, config}, _, _, state) do + defp format_message({:alert, config}, _, _) do %{route_id: route_id, direction_id: direction_id} = config.sources |> List.first() route = @@ -737,18 +713,18 @@ defmodule Signs.Bus do dest = headsign_abbreviation( - state.routes_engine.route_destination(route_id, direction_id), + RealtimeSigns.route_engine().route_destination(route_id, direction_id), @line_max - String.length(route) - String.length(no_svc) - 1 ) Content.Utilities.width_padded_string("#{route}#{dest}", no_svc, @line_max) end - defp format_long_message({:predictions, [single]}, current_time, _state) do + defp format_long_message({:predictions, [single]}, current_time) do [[format_prediction(single, nil, current_time), ""]] end - defp format_long_message({:predictions, [first, second | _]}, current_time, _state) do + defp format_long_message({:predictions, [first, second | _]}, current_time) do [ [ format_prediction(first, second, current_time), @@ -757,8 +733,8 @@ defmodule Signs.Bus do ] end - defp format_long_message({:alert, _} = message, current_time, state) do - [[format_message(message, nil, current_time, state), ""]] + defp format_long_message({:alert, _} = message, current_time) do + [[format_message(message, nil, current_time), ""]] end # Returns a string representation of a prediction, suitable for displaying on a sign. @@ -798,8 +774,8 @@ defmodule Signs.Bus do else: first.route_id end - defp config_headsign(%{sources: [first | _]}, state) do - state.routes_engine.route_destination(first.route_id, first.direction_id) + defp config_headsign(%{sources: [first | _]}) do + RealtimeSigns.route_engine().route_destination(first.route_id, first.direction_id) end defp headsign_abbreviation(headsign, max_size) do @@ -846,7 +822,7 @@ defmodule Signs.Bus do defp add_tts_preamble(str), do: "Upcoming departures: " <> str # Returns a list of audio tokens describing the given message. - defp message_audio({:predictions, [prediction | _]}, current_time, _state) do + defp message_audio({:predictions, [prediction | _]}, current_time) do route = case PaEss.Utilities.prediction_route_name(prediction) do nil -> [] @@ -865,19 +841,19 @@ defmodule Signs.Bus do Enum.concat([route, dest, time]) end - defp message_audio({:alert, config}, _, state) do + defp message_audio({:alert, config}, _) do route = case config_route_name(config) do nil -> [] str -> [{:route, str}] end - headsign = config_headsign(config, state) + headsign = config_headsign(config) route ++ [{:headsign, headsign}, :no_service] end - defp message_tts_audio({:predictions, [prediction | _]}, current_time, _state) do + defp message_tts_audio({:predictions, [prediction | _]}, current_time) do route = case PaEss.Utilities.prediction_route_name(prediction) do nil -> "" @@ -894,20 +870,20 @@ defmodule Signs.Bus do "#{route}#{prediction.headsign}, #{time}." end - defp message_tts_audio({:alert, config}, _, state) do + defp message_tts_audio({:alert, config}, _) do route = case config_route_name(config) do nil -> "" name -> "Route #{name}, " end - headsign = config_headsign(config, state) + headsign = config_headsign(config) "#{route}#{headsign}, no service." end # Returns a list of audio tokens representing the special "long form" description of # the given prediction. - defp long_message_audio({:predictions, predictions}, current_time, _state) do + defp long_message_audio({:predictions, predictions}, current_time) do Stream.take(predictions, 2) |> Enum.zip_with([:next, :following], fn prediction, next_or_following -> preamble = @@ -931,19 +907,19 @@ defmodule Signs.Bus do end) end - defp long_message_audio({:alert, config}, _, state) do + defp long_message_audio({:alert, config}, _) do route = case config_route_name(config) do nil -> [] str -> [{:route, str}] end - headsign = config_headsign(config, state) + headsign = config_headsign(config) [Enum.concat([[:there_is_no], route, [:bus_service_to, {:headsign, headsign}]])] end - defp long_message_tts_audio({:predictions, predictions}, current_time, _state) do + defp long_message_tts_audio({:predictions, predictions}, current_time) do Stream.take(predictions, 2) |> Enum.zip_with(["next", "following"], fn prediction, next_or_following -> route = @@ -964,14 +940,14 @@ defmodule Signs.Bus do |> Enum.join(" ") end - defp long_message_tts_audio({:alert, config}, _, state) do + defp long_message_tts_audio({:alert, config}, _) do route = case config_route_name(config) do nil -> "" name -> "route #{name} " end - headsign = config_headsign(config, state) + headsign = config_headsign(config) "There is no #{route}bus service to #{headsign}." end @@ -992,10 +968,10 @@ defmodule Signs.Bus do end defp send_audio(audios, tts_audios, state) do - %{audio_zones: audio_zones, sign_updater: sign_updater} = state + %{audio_zones: audio_zones} = state if audios != [] && audio_zones != [] do - sign_updater.play_message( + RealtimeSigns.sign_updater().play_message( state, audios, tts_audios, diff --git a/lib/signs/realtime.ex b/lib/signs/realtime.ex index 2e8b639a0..2e530c5a6 100644 --- a/lib/signs/realtime.ex +++ b/lib/signs/realtime.ex @@ -21,13 +21,6 @@ defmodule Signs.Realtime do :source_config, :current_content_top, :current_content_bottom, - :prediction_engine, - :location_engine, - :headway_engine, - :config_engine, - :alerts_engine, - :last_trip_engine, - :sign_updater, :last_update, :tick_read, :read_period_seconds @@ -68,14 +61,7 @@ defmodule Signs.Realtime do default_mode: Engine.Config.sign_config(), current_content_top: Content.Message.value(), current_content_bottom: Content.Message.value(), - prediction_engine: module(), - location_engine: module(), - headway_engine: module(), - config_engine: module(), - alerts_engine: module(), - last_trip_engine: module(), current_time_fn: fun(), - sign_updater: module(), last_update: DateTime.t(), tick_read: non_neg_integer(), read_period_seconds: non_neg_integer(), @@ -107,17 +93,10 @@ defmodule Signs.Realtime do config |> Map.get("default_mode") |> then(&if(&1 == "auto", do: :auto, else: :off)), current_content_top: "", current_content_bottom: "", - prediction_engine: Engine.Predictions, - location_engine: Engine.Locations, - headway_engine: Engine.ScheduledHeadways, - config_engine: Engine.Config, - alerts_engine: Engine.Alerts, - last_trip_engine: Engine.LastTrip, current_time_fn: fn -> time_zone = Application.get_env(:realtime_signs, :time_zone) DateTime.utc_now() |> DateTime.shift_zone!(time_zone) end, - sign_updater: PaEss.Updater, last_update: nil, tick_read: 240 + Map.fetch!(config, "read_loop_offset"), read_period_seconds: 240, @@ -142,19 +121,21 @@ defmodule Signs.Realtime do end def handle_info(:run_loop, sign) do - sign_config = sign.config_engine.sign_config(sign.id, sign.default_mode) + sign_config = RealtimeSigns.config_engine().sign_config(sign.id, sign.default_mode) current_time = sign.current_time_fn.() alert_status = map_source_config(sign.source_config, fn config -> stop_ids = SourceConfig.sign_stop_ids(config) routes = SourceConfig.sign_routes(config) - sign.alerts_engine.max_stop_status(stop_ids, routes) + RealtimeSigns.alert_engine().max_stop_status(stop_ids, routes) end) first_scheduled_departures = map_source_config(sign.source_config, fn config -> - sign.headway_engine.get_first_scheduled_departure(SourceConfig.sign_stop_ids(config)) + RealtimeSigns.headway_engine().get_first_scheduled_departure( + SourceConfig.sign_stop_ids(config) + ) end) prev_predictions_lookup = @@ -165,19 +146,19 @@ defmodule Signs.Realtime do {predictions, all_predictions} = case sign.source_config do {top, bottom} -> - top_predictions = fetch_predictions(top, prev_predictions_lookup, sign) - bottom_predictions = fetch_predictions(bottom, prev_predictions_lookup, sign) + top_predictions = fetch_predictions(top, prev_predictions_lookup) + bottom_predictions = fetch_predictions(bottom, prev_predictions_lookup) {{top_predictions, bottom_predictions}, top_predictions ++ bottom_predictions} config -> - predictions = fetch_predictions(config, prev_predictions_lookup, sign) + predictions = fetch_predictions(config, prev_predictions_lookup) {predictions, predictions} end service_end_statuses_per_source = map_source_config( sign.source_config, - &has_service_ended_for_source?(sign, &1, current_time) + &has_service_ended_for_source?(&1, current_time) ) {new_top, new_bottom} = @@ -209,21 +190,21 @@ defmodule Signs.Realtime do {:noreply, state} end - defp has_service_ended_for_source?(sign, source, current_time) do + defp has_service_ended_for_source?(source, current_time) do if source.headway_group not in ["red_trunk", "red_ashmont", "red_braintree"] do SourceConfig.sign_stop_ids(source) - |> Enum.count(&has_last_trip_departed_stop?(&1, sign, current_time)) >= 1 + |> Enum.count(&has_last_trip_departed_stop?(&1, current_time)) >= 1 else false end end - defp has_last_trip_departed_stop?(stop_id, sign, current_time) do - sign.last_trip_engine.get_recent_departures(stop_id) + defp has_last_trip_departed_stop?(stop_id, current_time) do + RealtimeSigns.last_trip_engine().get_recent_departures(stop_id) |> Enum.any?(fn {trip_id, departure_time} -> # Use a 3 second buffer to make sure trips have fully departed DateTime.to_unix(current_time) - DateTime.to_unix(departure_time) > 3 and - sign.last_trip_engine.is_last_trip?(trip_id) + RealtimeSigns.last_trip_engine().is_last_trip?(trip_id) end) end @@ -231,9 +212,10 @@ defmodule Signs.Realtime do Map.take(prediction, [:stop_id, :route_id, :vehicle_id, :direction_id, :trip_id]) end - defp fetch_predictions(%{sources: sources}, prev_predictions_lookup, state) do + defp fetch_predictions(%{sources: sources}, prev_predictions_lookup) do for source <- sources, - prediction <- state.prediction_engine.for_stop(source.stop_id, source.direction_id) do + prediction <- + RealtimeSigns.prediction_engine().for_stop(source.stop_id, source.direction_id) do prev = prev_predictions_lookup[prediction_key(prediction)] prediction diff --git a/lib/signs/utilities/audio.ex b/lib/signs/utilities/audio.ex index ec75080fa..e2a5bd62a 100644 --- a/lib/signs/utilities/audio.ex +++ b/lib/signs/utilities/audio.ex @@ -173,7 +173,7 @@ defmodule Signs.Utilities.Audio do announce_arriving?(sign, message) && message.prediction.route_id in @heavy_rail_routes -> crowding = Signs.Utilities.Crowding.crowding_description(message.prediction, sign) - new_cars? = PaEss.Utilities.prediction_new_cars?(message.prediction, sign) + new_cars? = PaEss.Utilities.prediction_new_cars?(message.prediction) {Audio.Approaching.from_message(message, crowding, new_cars?), sign @@ -361,7 +361,7 @@ defmodule Signs.Utilities.Audio do @spec send_audio(Signs.Realtime.t() | Signs.Bus.t(), [Content.Audio.t()]) :: :ok def send_audio(sign, audios) do - sign.sign_updater.play_message( + RealtimeSigns.sign_updater().play_message( sign, Enum.map(audios, &Content.Audio.to_params(&1)), Enum.map(audios, &Content.Audio.to_tts(&1)), diff --git a/lib/signs/utilities/crowding.ex b/lib/signs/utilities/crowding.ex index c41dc2a92..0b0caec8c 100644 --- a/lib/signs/utilities/crowding.ex +++ b/lib/signs/utilities/crowding.ex @@ -5,8 +5,8 @@ defmodule Signs.Utilities.Crowding do | nil def crowding_description(_, %{source_config: {_, _}}), do: nil - def crowding_description(%{route_id: "Orange"} = prediction, sign) do - case sign.location_engine.for_vehicle(prediction.vehicle_id) do + def crowding_description(%{route_id: "Orange"} = prediction, _sign) do + case RealtimeSigns.location_engine().for_vehicle(prediction.vehicle_id) do %Locations.Location{ stop_id: stop_id, status: status, diff --git a/lib/signs/utilities/headways.ex b/lib/signs/utilities/headways.ex index 61fc1ef7d..9b0dd877a 100644 --- a/lib/signs/utilities/headways.ex +++ b/lib/signs/utilities/headways.ex @@ -6,10 +6,10 @@ defmodule Signs.Utilities.Headways do alias Signs.Utilities.SourceConfig require Logger - @spec headway_messages(Signs.Realtime.t(), SourceConfig.config(), DateTime.t()) :: + @spec headway_messages(SourceConfig.config(), DateTime.t()) :: Signs.Realtime.sign_messages() | nil - def headway_messages(sign, config, current_time) do - case fetch_headways(sign, config.headway_group, config.sources, current_time) do + def headway_messages(config, current_time) do + case fetch_headways(config.headway_group, config.sources, current_time) do nil -> nil @@ -21,10 +21,10 @@ defmodule Signs.Utilities.Headways do end end - @spec headway_message(Signs.Realtime.t(), SourceConfig.config(), DateTime.t()) :: + @spec headway_message(SourceConfig.config(), DateTime.t()) :: Signs.Realtime.line_content() | nil - def headway_message(sign, config, current_time) do - case fetch_headways(sign, config.headway_group, config.sources, current_time) do + def headway_message(config, current_time) do + case fetch_headways(config.headway_group, config.sources, current_time) do nil -> nil @@ -37,13 +37,13 @@ defmodule Signs.Utilities.Headways do end end - defp fetch_headways(sign, headway_group, sources, current_time) do + defp fetch_headways(headway_group, sources, current_time) do stop_ids = Enum.map(sources, & &1.stop_id) with headways when not is_nil(headways) <- - sign.config_engine.headway_config(headway_group, current_time), + RealtimeSigns.config_engine().headway_config(headway_group, current_time), true <- - sign.headway_engine.display_headways?(stop_ids, current_time, headways) do + RealtimeSigns.headway_engine().display_headways?(stop_ids, current_time, headways) do headways else _ -> nil diff --git a/lib/signs/utilities/messages.ex b/lib/signs/utilities/messages.ex index cf7850dbb..3d707d33d 100644 --- a/lib/signs/utilities/messages.ex +++ b/lib/signs/utilities/messages.ex @@ -53,7 +53,7 @@ defmodule Signs.Utilities.Messages do Signs.Utilities.Predictions.prediction_message(predictions, config, sign) || service_ended_message(service_status, config) || alert_message(alert_status, sign, config) || - Signs.Utilities.Headways.headway_message(sign, config, current_time) || + Signs.Utilities.Headways.headway_message(config, current_time) || early_am_message(current_time, scheduled, config) || %Content.Message.Empty{} end) @@ -71,7 +71,7 @@ defmodule Signs.Utilities.Messages do Signs.Utilities.Predictions.prediction_messages(predictions, config, sign) || service_ended_messages(service_status, config) || alert_messages(alert_status, sign, config) || - Signs.Utilities.Headways.headway_messages(sign, config, current_time) || + Signs.Utilities.Headways.headway_messages(config, current_time) || early_am_messages(current_time, scheduled, config) || {%Content.Message.Empty{}, %Content.Message.Empty{}} end diff --git a/lib/signs/utilities/updater.ex b/lib/signs/utilities/updater.ex index 15bee1aa6..0630f412b 100644 --- a/lib/signs/utilities/updater.ex +++ b/lib/signs/utilities/updater.ex @@ -22,7 +22,7 @@ defmodule Signs.Utilities.Updater do Timex.after?(current_time, Timex.shift(sign.last_update, seconds: 130)) || sign.current_content_top != new_top || sign.current_content_bottom != new_bottom do - sign.sign_updater.set_background_message(sign, new_top, new_bottom) + RealtimeSigns.sign_updater().set_background_message(sign, new_top, new_bottom) %{ sign diff --git a/test/engine/config_test.exs b/test/engine/config_test.exs index 758bedfad..2166bcd2e 100644 --- a/test/engine/config_test.exs +++ b/test/engine/config_test.exs @@ -3,25 +3,11 @@ defmodule Engine.ConfigTest do import ExUnit.CaptureLog describe "sign_config/2" do - test "is auto when the sign is enabled" do - Engine.Config.update() - - Process.sleep(50) - assert Engine.Config.sign_config("chelsea_inbound", :off) == :auto - end - - test "is off when the sign is disabled" do - Engine.Config.update() - - Process.sleep(50) - assert Engine.Config.sign_config("chelsea_outbound", :auto) == :off - end - test "is the provided default value when the sign is unspecified" do - Engine.Config.update() + state = initialize_test_state(%{}) - Process.sleep(50) - assert Engine.Config.sign_config("unspecified_sign", :headway) == :headway + assert Engine.Config.sign_config(state.table_name_signs, "unspecified_sign", :headway) == + :headway end test "returns custom text when it's not expired" do @@ -48,13 +34,6 @@ defmodule Engine.ConfigTest do assert Engine.Config.sign_config(state.table_name_signs, "custom_text_test", :off) == :auto end - - test "properly returns headway mode" do - Engine.Config.update() - - Process.sleep(50) - assert Engine.Config.sign_config("headway_test", :off) == :headway - end end describe "handle_info/2" do diff --git a/test/engine/predictions_test.exs b/test/engine/predictions_test.exs index b3e268ef2..64c2881be 100644 --- a/test/engine/predictions_test.exs +++ b/test/engine/predictions_test.exs @@ -2,6 +2,7 @@ defmodule Engine.PredictionsTest do use ExUnit.Case import ExUnit.CaptureLog import Engine.Predictions + import Mox describe "handle_info/2" do test "keeps existing states when trip_update url has not been modified" do @@ -37,6 +38,7 @@ defmodule Engine.PredictionsTest do end test "instead of deleting old predictions, overwrites them with []" do + stub(Engine.Locations.Mock, :for_vehicle, fn _ -> nil end) trip_update_url = Application.get_env(:realtime_signs, :trip_update_url) Application.put_env(:realtime_signs, :trip_update_url, "fake_trip_update2.json") diff --git a/test/engine/scheduled_headways_test.exs b/test/engine/scheduled_headways_test.exs index a2f577e08..dc13e0f22 100644 --- a/test/engine/scheduled_headways_test.exs +++ b/test/engine/scheduled_headways_test.exs @@ -235,12 +235,6 @@ defmodule Engine.ScheduledHeadwaysTest do headways = %Engine.Config.Headway{headway_id: {"test", :peak}, range_high: 0, range_low: 0} refute Engine.ScheduledHeadways.display_headways?(:no_data, ["no_stop"], time, headways) end - - test "display_headways?/3 fills in ETS table name" do - time = DateTime.from_naive!(~N[2020-03-20 10:00:00], "America/New_York") - headways = %Engine.Config.Headway{headway_id: {"test", :peak}, range_high: 0, range_low: 0} - refute Engine.ScheduledHeadways.display_headways?(["no_data"], time, headways) - end end describe "data_update callback" do @@ -351,7 +345,7 @@ defmodule Engine.ScheduledHeadwaysTest do {:noreply, _updated_state} = Engine.ScheduledHeadways.handle_info(:data_update, state) - [{first, last}] = + [{first, _}] = Engine.ScheduledHeadways.get_first_last_departures(:first_last_departures_test4, ["123"]) # Ensure that after parsing and loading datetimes we can use them with native functions. diff --git a/test/headway_analysis/server_test.exs b/test/headway_analysis/server_test.exs index f4af21dba..ab1fc263c 100644 --- a/test/headway_analysis/server_test.exs +++ b/test/headway_analysis/server_test.exs @@ -7,10 +7,7 @@ defmodule HeadwayAnalysys.ServerTest do sign_id: "test_sign", headway_group: "test_headway_group", stop_ids: ["1"], - vehicles_present: MapSet.new(["v-1"]), - prediction_engine: Engine.Predictions.Mock, - config_engine: Engine.Config.Mock, - location_engine: Engine.Locations.Mock + vehicles_present: MapSet.new(["v-1"]) } setup :verify_on_exit! diff --git a/test/predictions/predictions_test.exs b/test/predictions/predictions_test.exs index 5e7418e04..f5e81fd55 100644 --- a/test/predictions/predictions_test.exs +++ b/test/predictions/predictions_test.exs @@ -1,7 +1,7 @@ defmodule Predictions.PredictionsTest do use ExUnit.Case import Predictions.Predictions - import Test.Support.Helpers + import Mox @current_time Timex.to_datetime(~N[2017-04-07 09:00:00], "America/New_York") @feed_message %{ @@ -62,6 +62,11 @@ defmodule Predictions.PredictionsTest do } describe "get_all/2" do + setup do + stub(Engine.Locations.Mock, :for_vehicle, fn _ -> nil end) + :ok + end + test "finds predictions for one trip" do expected = %{ {"70261", 0} => [ diff --git a/test/signs/bus_test.exs b/test/signs/bus_test.exs index 2d60fbc63..2245c215b 100644 --- a/test/signs/bus_test.exs +++ b/test/signs/bus_test.exs @@ -2,99 +2,6 @@ defmodule Signs.BusTest do use ExUnit.Case, async: true import Mox - defmodule FakeBusPredictions do - def predictions_for_stop("stop1") do - [ - %Predictions.BusPrediction{ - direction_id: 0, - departure_time: Timex.shift(Timex.now(), minutes: 2), - route_id: "14", - stop_id: "stop1", - headsign: "Wakefield Ave", - vehicle_id: "a", - trip_id: "a", - updated_at: "" - }, - %Predictions.BusPrediction{ - direction_id: 0, - departure_time: Timex.shift(Timex.now(), minutes: 11), - route_id: "14", - stop_id: "stop1", - headsign: "Wakefield Ave", - vehicle_id: "b", - trip_id: "b", - updated_at: "" - }, - %Predictions.BusPrediction{ - direction_id: 1, - departure_time: Timex.shift(Timex.now(), minutes: 7), - route_id: "34", - stop_id: "stop1", - headsign: "Clarendon Hill", - vehicle_id: "c", - trip_id: "c", - updated_at: "" - }, - %Predictions.BusPrediction{ - direction_id: 1, - departure_time: Timex.shift(Timex.now(), minutes: 4), - route_id: "741", - stop_id: "stop1", - headsign: "Chelsea", - vehicle_id: "d", - trip_id: "d", - updated_at: "" - } - ] - end - - def predictions_for_stop("stop2") do - [ - %Predictions.BusPrediction{ - direction_id: 0, - departure_time: Timex.shift(Timex.now(), minutes: 8), - route_id: "749", - stop_id: "stop2", - headsign: "Nubian", - vehicle_id: "e", - trip_id: "e", - updated_at: "" - } - ] - end - - def predictions_for_stop("stop3") do - [] - end - end - - defmodule FakeConfig do - def sign_config("auto_sign", _default), do: :auto - def sign_config("off_sign", _default), do: :off - def sign_config("headway", _default), do: :headway - def sign_config("static_sign", _default), do: {:static_text, {"custom", "message"}} - def chelsea_bridge_config(), do: :auto - end - - defmodule FakeChelseaBridge do - def bridge_status(), do: %{raised?: false, estimate: nil} - end - - defmodule FakeChelseaBridgeRaised do - def bridge_status(), do: %{raised?: true, estimate: Timex.shift(Timex.now(), minutes: 4)} - end - - defmodule FakeAlerts do - def stop_status("stop3"), do: :station_closure - def stop_status(_), do: :none - def route_status("51"), do: :suspension_closed_station - def route_status(_), do: :none - end - - defmodule FakeRoutes do - def route_destination("51", 0), do: "Reservoir Station" - end - @sign_state %Signs.Bus{ id: "auto_sign", pa_ess_loc: "ABCD", @@ -109,12 +16,6 @@ defmodule Signs.BusTest do chelsea_bridge: nil, read_loop_interval: 360, read_loop_offset: 30, - config_engine: FakeConfig, - prediction_engine: FakeBusPredictions, - bridge_engine: FakeChelseaBridge, - alerts_engine: FakeAlerts, - routes_engine: FakeRoutes, - sign_updater: PaEss.Updater.Mock, prev_predictions: [], prev_bridge_status: nil, current_messages: {nil, nil}, @@ -126,6 +27,80 @@ defmodule Signs.BusTest do setup :verify_on_exit! describe "run loop" do + setup do + stub(Engine.BusPredictions.Mock, :predictions_for_stop, fn + "stop1" -> + [ + prediction( + departure: 2, + stop_id: "stop1", + direction_id: 0, + route_id: "14", + headsign: "Wakefield Ave" + ), + prediction( + departure: 11, + stop_id: "stop1", + direction_id: 0, + route_id: "14", + headsign: "Wakefield Ave" + ), + prediction( + departure: 7, + stop_id: "stop1", + direction_id: 1, + route_id: "34", + headsign: "Clarendon Hill" + ), + prediction( + departure: 4, + stop_id: "stop1", + direction_id: 1, + route_id: "741", + headsign: "Chelsea" + ) + ] + + "stop2" -> + [ + prediction( + departure: 8, + stop_id: "stop2", + direction_id: 0, + route_id: "749", + headsign: "Nubian" + ) + ] + + "stop3" -> + [] + end) + + stub(Engine.Config.Mock, :sign_config, fn + "auto_sign", _default -> :auto + "off_sign", _default -> :off + "headway", _default -> :headway + "static_sign", _default -> {:static_text, {"custom", "message"}} + end) + + stub(Engine.Config.Mock, :chelsea_bridge_config, fn -> :auto end) + + stub(Engine.Alerts.Mock, :stop_status, fn + "stop3" -> :station_closure + _ -> :none + end) + + stub(Engine.Alerts.Mock, :route_status, fn + "51" -> :suspension_closed_station + _ -> :none + end) + + stub(Engine.ChelseaBridge.Mock, :bridge_status, fn -> %{raised?: false, estimate: nil} end) + stub(Engine.Routes.Mock, :route_destination, fn "51", 0 -> "Reservoir Station" end) + + :ok + end + test "platform mode, top two" do expect_messages(["14 WakfldAv 2 min", "14 WakfldAv 11 min"]) @@ -272,6 +247,10 @@ defmodule Signs.BusTest do end test "bridge raised" do + expect(Engine.ChelseaBridge.Mock, :bridge_status, fn -> + %{raised?: true, estimate: Timex.shift(Timex.now(), minutes: 4)} + end) + expect_messages([ [{"14 WakfldAv 2 min", 6}, {"Drawbridge is up", 6}], [{"14 WakfldAv 11 min", 6}, {"SL3 delays 4 more min", 6}] @@ -329,14 +308,17 @@ defmodule Signs.BusTest do state = Map.merge(@sign_state, %{ configs: [%{sources: [%{stop_id: "stop1", route_id: "14", direction_id: 0}]}], - chelsea_bridge: "audio_visual", - bridge_engine: FakeChelseaBridgeRaised + chelsea_bridge: "audio_visual" }) Signs.Bus.handle_info(:run_loop, state) end test "standalone bridge announcement" do + expect(Engine.ChelseaBridge.Mock, :bridge_status, fn -> + %{raised?: true, estimate: Timex.shift(Timex.now(), minutes: 4)} + end) + expect_audios( [ {:canned, {"135", ["5504"], :audio_visual}}, @@ -367,7 +349,6 @@ defmodule Signs.BusTest do Map.merge(@sign_state, %{ configs: [%{sources: [%{stop_id: "stop1", route_id: "14", direction_id: 0}]}], chelsea_bridge: "audio", - bridge_engine: FakeChelseaBridgeRaised, prev_bridge_status: %{raised?: false, estimate: nil}, current_messages: {"14 WakfldAv 2 min", "14 WakfldAv 11 min"}, last_update: Timex.shift(Timex.now(), seconds: -40), @@ -445,4 +426,17 @@ defmodule Signs.BusTest do :ok end) end + + defp prediction(opts) do + %Predictions.BusPrediction{ + direction_id: opts[:direction_id], + departure_time: Timex.shift(Timex.now(), minutes: opts[:departure]), + route_id: opts[:route_id], + stop_id: opts[:stop_id], + headsign: opts[:headsign], + vehicle_id: "a", + trip_id: "a", + updated_at: "" + } + end end diff --git a/test/signs/realtime_test.exs b/test/signs/realtime_test.exs index 50701c966..a50baf57e 100644 --- a/test/signs/realtime_test.exs +++ b/test/signs/realtime_test.exs @@ -38,14 +38,7 @@ defmodule Signs.RealtimeTest do }, current_content_top: "Southbound trains", current_content_bottom: "Every 11 to 13 min", - prediction_engine: Engine.Predictions.Mock, - location_engine: Engine.Locations.Mock, - headway_engine: Engine.ScheduledHeadways.Mock, - config_engine: Engine.Config.Mock, - alerts_engine: Engine.Alerts.Mock, - last_trip_engine: Engine.LastTrip.Mock, current_time_fn: &Signs.RealtimeTest.fake_time_fn/0, - sign_updater: PaEss.Updater.Mock, last_update: @fake_time, tick_read: 1, read_period_seconds: 100, diff --git a/test/test_helper.exs b/test/test_helper.exs index e117e0b66..1e314d61b 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,11 +1,14 @@ Application.ensure_all_started(:inets) -ExUnit.start() +ExUnit.start(capture_log: true) Mox.defmock(Engine.NetworkCheck.Mock, for: Engine.NetworkCheck) -Mox.defmock(PaEss.Updater.Mock, for: PaEss.UpdaterAPI) -Mox.defmock(Engine.Config.Mock, for: Engine.ConfigAPI) -Mox.defmock(Engine.Alerts.Mock, for: Engine.AlertsAPI) -Mox.defmock(Engine.Predictions.Mock, for: Engine.PredictionsAPI) -Mox.defmock(Engine.ScheduledHeadways.Mock, for: Engine.ScheduledHeadwaysAPI) -Mox.defmock(Engine.Locations.Mock, for: Engine.LocationsAPI) -Mox.defmock(Engine.LastTrip.Mock, for: Engine.LastTripAPI) +Mox.defmock(PaEss.Updater.Mock, for: PaEss.Updater) +Mox.defmock(Engine.Config.Mock, for: Engine.Config) +Mox.defmock(Engine.Alerts.Mock, for: Engine.Alerts) +Mox.defmock(Engine.Predictions.Mock, for: Engine.Predictions) +Mox.defmock(Engine.ScheduledHeadways.Mock, for: Engine.ScheduledHeadways) +Mox.defmock(Engine.Locations.Mock, for: Engine.Locations) +Mox.defmock(Engine.LastTrip.Mock, for: Engine.LastTrip) +Mox.defmock(Engine.ChelseaBridge.Mock, for: Engine.ChelseaBridge) +Mox.defmock(Engine.Routes.Mock, for: Engine.Routes) +Mox.defmock(Engine.BusPredictions.Mock, for: Engine.BusPredictions)