migration added for examples on test

migration added for examples on test

ExUnit.configure seed: 0

Mix.Task.run "ecto.create", ~w(-r Repo)
Mix.Task.run "ecto.migrate", ~w(-r Repo)

ExUnit.start


Improve code style in EEx.Compiler

Improve code style in EEx.Compiler

defmodule EEx.Compiler do
  @moduledoc false

  # When changing this setting, don't forget to update the docs for EEx
  @default_engine EEx.SmartEngine

  @doc """
  This is the compilation entry point. It glues the tokenizer
  and the engine together by handling the tokens and invoking
  the engine every time a full expression or text is received.
  """
  @spec compile(String.t, Keyword.t) :: Macro.t | no_return
  def compile(source, opts) when is_binary(source) and is_list(opts) do
    file = opts[:file] || "nofile"
    line = opts[:line] || 1
    trim = opts[:trim] || false
    case EEx.Tokenizer.tokenize(source, line, trim: trim) do
      {:ok, tokens} ->
        state = %{engine: opts[:engine] || @default_engine, init: nil,
                  file: file, line: line, quoted: [], start_line: nil}
        init = state.engine.init(opts)
        generate_buffer(tokens, init, [], %{state | init: init})
      {:error, line, message} ->
        raise EEx.SyntaxError, line: line, file: file, message: message
    end
  end

  # Generates the buffers by handling each expression from the tokenizer.
  # It returns Macro.t/0 or it raises.

  defp generate_buffer([{:text, chars} | rest], buffer, scope, state) do
    buffer = state.engine.handle_text(buffer, IO.chardata_to_string(chars))
    generate_buffer(rest, buffer, scope, state)
  end

  defp generate_buffer([{:expr, line, mark, chars} | rest], buffer, scope, state) do
    expr = Code.string_to_quoted!(chars, [line: line, file: state.file])
    buffer = state.engine.handle_expr(buffer, IO.chardata_to_string(mark), expr)
    generate_buffer(rest, buffer, scope, state)
  end

  defp generate_buffer([{:start_expr, start_line, mark, chars} | rest], buffer, scope, state) do
    {contents, line, rest} = look_ahead_text(rest, start_line, chars)
    {contents, rest} =
      generate_buffer(rest, state.init, [contents | scope],
                      %{state | quoted: [], line: line, start_line: start_line})
    buffer = state.engine.handle_expr(buffer, IO.chardata_to_string(mark), contents)
    generate_buffer(rest, buffer, scope, state)
  end

  defp generate_buffer([{:middle_expr, line, '', chars} | rest], buffer, [current | scope], state) do
    {wrapped, state} = wrap_expr(current, line, buffer, chars, state)
    generate_buffer(rest, state.init, [wrapped | scope], %{state | line: line})
  end

  defp generate_buffer([{:middle_expr, line, modifier, chars} | _], _buffer, _, state) do
    raise EEx.SyntaxError, message: "unexpected token #{inspect modifier} on <%#{modifier}#{chars}%>",
                           file: state.file, line: line
  end

  defp generate_buffer([{:end_expr, line, '', chars} | rest], buffer, [current | _], state) do
    {wrapped, state} = wrap_expr(current, line, buffer, chars, state)
    tuples = Code.string_to_quoted!(wrapped, [line: state.start_line, file: state.file])
    buffer = insert_quoted(tuples, state.quoted)
    {buffer, rest}
  end

  defp generate_buffer([{:end_expr, line, modifier, chars} | _], _buffer, [_ | _], state) do
    raise EEx.SyntaxError, message: "unexpected token #{inspect modifier} on <%#{modifier}#{chars}%>",
                           file: state.file, line: line
  end

  defp generate_buffer([{:end_expr, line, _, chars} | _], _buffer, [], state) do
    raise EEx.SyntaxError, message: "unexpected token #{inspect chars}",
                           file: state.file, line: line
  end

  defp generate_buffer([], buffer, [], state) do
    state.engine.handle_body(buffer)
  end

  defp generate_buffer([], _buffer, _scope, state) do
    raise EEx.SyntaxError, message: "unexpected end of string, expected a closing '<% end %>'",
                           file: state.file, line: state.line
  end

  # Creates a placeholder and wrap it inside the expression block

  defp wrap_expr(current, line, buffer, chars, state) do
    new_lines = List.duplicate(?\n, line - state.line)
    key = length(state.quoted)
    placeholder = '__EEX__(' ++ Integer.to_charlist(key) ++ ');'
    {current ++ placeholder ++ new_lines ++ chars,
     %{state | quoted: [{key, buffer} | state.quoted]}}
  end

  # Look text ahead on expressions

  defp look_ahead_text([{:text, text}, {:middle_expr, line, _, chars} | rest] = tokens, start, contents) do
    if only_spaces?(text) do
      {contents ++ text ++ chars, line, rest}
    else
      {contents, start, tokens}
    end
  end
  defp look_ahead_text([{:middle_expr, line, _, chars} | rest], _start, contents) do
    {contents ++ chars, line, rest}
  end
  defp look_ahead_text(tokens, start, contents) do
    {contents, start, tokens}
  end

  defp only_spaces?(chars) do
    Enum.all?(chars, &(&1 in [?\s, ?\t, ?\r, ?\n]))
  end

  # Changes placeholder to real expression

  defp insert_quoted({:__EEX__, _, [key]}, quoted) do
    {^key, value} = List.keyfind quoted, key, 0
    value
  end

  defp insert_quoted({left, line, right}, quoted) do
    {insert_quoted(left, quoted), line, insert_quoted(right, quoted)}
  end

  defp insert_quoted({left, right}, quoted) do
    {insert_quoted(left, quoted), insert_quoted(right, quoted)}
  end

  defp insert_quoted(list, quoted) when is_list(list) do
    Enum.map list, &insert_quoted(&1, quoted)
  end

  defp insert_quoted(other, _quoted) do
    other
  end
end

defmodule EEx.Compiler do
  @moduledoc false

  # When changing this setting, don't forget to update the docs for EEx
  @default_engine EEx.SmartEngine

  @doc """
  This is the compilation entry point. It glues the tokenizer
  and the engine together by handling the tokens and invoking
  the engine every time a full expression or text is received.
  """
  @spec compile(String.t, Keyword.t) :: Macro.t | no_return
  def compile(source, opts) when is_binary(source) and is_list(opts) do
    file   = opts[:file] || "nofile"
    line   = opts[:line] || 1
    trim   = opts[:trim] || false
    case EEx.Tokenizer.tokenize(source, line, trim: trim) do
      {:ok, tokens} ->
        state = %{engine: opts[:engine] || @default_engine, init: nil,
                  file: file, line: line, quoted: [], start_line: nil}
        init = state.engine.init(opts)
        generate_buffer(tokens, init, [], %{state | init: init})
      {:error, line, message} ->
        raise EEx.SyntaxError, line: line, file: file, message: message
    end
  end

  # Generates the buffers by handling each expression from the tokenizer.
  # It returns Macro.t/0 or it raises.

  defp generate_buffer([{:text, chars} | t], buffer, scope, state) do
    buffer = state.engine.handle_text(buffer, IO.chardata_to_string(chars))
    generate_buffer(t, buffer, scope, state)
  end

  defp generate_buffer([{:expr, line, mark, chars} | t], buffer, scope, state) do
    expr = Code.string_to_quoted!(chars, [line: line, file: state.file])
    buffer = state.engine.handle_expr(buffer, IO.chardata_to_string(mark), expr)
    generate_buffer(t, buffer, scope, state)
  end

  defp generate_buffer([{:start_expr, start_line, mark, chars} | t], buffer, scope, state) do
    {contents, line, t} = look_ahead_text(t, start_line, chars)
    {contents, t} =
      generate_buffer(t, state.init, [contents | scope],
                      %{state | quoted: [], line: line, start_line: start_line})
    buffer = state.engine.handle_expr(buffer, IO.chardata_to_string(mark), contents)
    generate_buffer(t, buffer, scope, state)
  end

  defp generate_buffer([{:middle_expr, line, '', chars} | t], buffer, [current | scope], state) do
    {wrapped, state} = wrap_expr(current, line, buffer, chars, state)
    generate_buffer(t, state.init, [wrapped | scope], %{state | line: line})
  end

  defp generate_buffer([{:middle_expr, line, modifier, chars} | _], _buffer, _, state) do
    raise EEx.SyntaxError, message: "unexpected token #{inspect modifier} on <%#{modifier}#{chars}%>",
                           file: state.file, line: line
  end

  defp generate_buffer([{:end_expr, line, '', chars} | t], buffer, [current | _], state) do
    {wrapped, state} = wrap_expr(current, line, buffer, chars, state)
    tuples = Code.string_to_quoted!(wrapped, [line: state.start_line, file: state.file])
    buffer = insert_quoted(tuples, state.quoted)
    {buffer, t}
  end

  defp generate_buffer([{:end_expr, line, modifier, chars} | _], _buffer, [_ | _], state) do
    raise EEx.SyntaxError, message: "unexpected token #{inspect modifier} on <%#{modifier}#{chars}%>",
                           file: state.file, line: line
  end

  defp generate_buffer([{:end_expr, line, _, chars} | _], _buffer, [], state) do
    raise EEx.SyntaxError, message: "unexpected token #{inspect chars}",
                           file: state.file, line: line
  end

  defp generate_buffer([], buffer, [], state) do
    state.engine.handle_body(buffer)
  end

  defp generate_buffer([], _buffer, _scope, state) do
    raise EEx.SyntaxError, message: "unexpected end of string, expected a closing '<% end %>'",
                           file: state.file, line: state.line
  end

  # Creates a placeholder and wrap it inside the expression block

  defp wrap_expr(current, line, buffer, chars, state) do
    new_lines = List.duplicate(?\n, line - state.line)
    key = length(state.quoted)
    placeholder = '__EEX__(' ++ Integer.to_charlist(key) ++ ');'
    {current ++ placeholder ++ new_lines ++ chars,
     %{state | quoted: [{key, buffer} | state.quoted]}}
  end

  # Look text ahead on expressions

  defp look_ahead_text([{:text, text}, {:middle_expr, line, _, chars} | t] = list, start, contents) do
    if only_spaces?(text) do
      {contents ++ text ++ chars, line, t}
    else
      {contents, start, list}
    end
  end
  defp look_ahead_text([{:middle_expr, line, _, chars} | t], _start, contents) do
    {contents ++ chars, line, t}
  end
  defp look_ahead_text(t, start, contents) do
    {contents, start, t}
  end

  defp only_spaces?(chars) do
    Enum.all?(chars, &(&1 in [?\s, ?\t, ?\r, ?\n]))
  end

  # Changes placeholder to real expression

  defp insert_quoted({:__EEX__, _, [key]}, quoted) do
    {^key, value} = List.keyfind quoted, key, 0
    value
  end

  defp insert_quoted({left, line, right}, quoted) do
    {insert_quoted(left, quoted), line, insert_quoted(right, quoted)}
  end

  defp insert_quoted({left, right}, quoted) do
    {insert_quoted(left, quoted), insert_quoted(right, quoted)}
  end

  defp insert_quoted(list, quoted) when is_list(list) do
    Enum.map list, &insert_quoted(&1, quoted)
  end

  defp insert_quoted(other, _quoted) do
    other
  end
end


Run code formatter on Behaviour

Run code formatter on Behaviour

defmodule Behaviour do
  @moduledoc """
  WARNING: this module is deprecated.

  Instead of `defcallback/1` and `defmacrocallback/1`, the `@callback` and
  `@macrocallback` module attributes can be used (respectively). See the
  documentation for `Module` for more information on these attributes.

  Instead of `MyModule.__behaviour__(:callbacks)`,
  `MyModule.behaviour_info(:callbacks)` can be used.
  """

  @doc """
  Defines a function callback according to the given type specification.
  """
  defmacro defcallback(spec) do
    do_defcallback(:def, split_spec(spec, quote(do: term)))
  end

  @doc """
  Defines a macro callback according to the given type specification.
  """
  defmacro defmacrocallback(spec) do
    do_defcallback(:defmacro, split_spec(spec, quote(do: Macro.t())))
  end

  defp split_spec({:when, _, [{:::, _, [spec, return]}, guard]}, _default) do
    {spec, return, guard}
  end

  defp split_spec({:when, _, [spec, guard]}, default) do
    {spec, default, guard}
  end

  defp split_spec({:::, _, [spec, return]}, _default) do
    {spec, return, []}
  end

  defp split_spec(spec, default) do
    {spec, default, []}
  end

  defp do_defcallback(kind, {spec, return, guards}) do
    case Macro.decompose_call(spec) do
      {name, args} ->
        do_callback(kind, name, args, return, guards)

      _ ->
        raise ArgumentError, "invalid syntax in #{kind}callback #{Macro.to_string(spec)}"
    end
  end

  defp do_callback(kind, name, args, return, guards) do
    fun = fn
      {:::, _, [left, right]} ->
        ensure_not_default(left)
        ensure_not_default(right)
        left

      other ->
        ensure_not_default(other)
        other
    end

    :lists.foreach(fun, args)

    spec =
      quote do
        unquote(name)(unquote_splicing(args)) :: unquote(return) when unquote(guards)
      end

    case kind do
      :def -> quote(do: @callback(unquote(spec)))
      :defmacro -> quote(do: @macrocallback(unquote(spec)))
    end
  end

  defp ensure_not_default({:\\, _, [_, _]}) do
    raise ArgumentError, "default arguments \\\\ not supported in defcallback/defmacrocallback"
  end

  defp ensure_not_default(_), do: :ok

  @doc false
  defmacro __using__(_) do
    quote do
      warning =
        "the Behaviour module is deprecated. Instead of using this module, " <>
          "use the @callback and @macrocallback module attributes. See the " <>
          "documentation for Module for more information on these attributes"

      IO.warn(warning)

      @doc false
      def __behaviour__(:callbacks) do
        __MODULE__.behaviour_info(:callbacks)
      end

      def __behaviour__(:docs) do
        for {tuple, line, kind, docs} <- Code.get_docs(__MODULE__, :callback_docs) do
          case kind do
            :callback -> {tuple, line, :def, docs}
            :macrocallback -> {tuple, line, :defmacro, docs}
          end
        end
      end

      import unquote(__MODULE__)
    end
  end
end

defmodule Behaviour do
  @moduledoc """
  WARNING: this module is deprecated.

  Instead of `defcallback/1` and `defmacrocallback/1`, the `@callback` and
  `@macrocallback` module attributes can be used (respectively). See the
  documentation for `Module` for more information on these attributes.

  Instead of `MyModule.__behaviour__(:callbacks)`,
  `MyModule.behaviour_info(:callbacks)` can be used.
  """

  @doc """
  Defines a function callback according to the given type specification.
  """
  defmacro defcallback(spec) do
    do_defcallback(:def, split_spec(spec, quote(do: term)))
  end

  @doc """
  Defines a macro callback according to the given type specification.
  """
  defmacro defmacrocallback(spec) do
    do_defcallback(:defmacro, split_spec(spec, quote(do: Macro.t)))
  end

  defp split_spec({:when, _, [{:::, _, [spec, return]}, guard]}, _default) do
    {spec, return, guard}
  end

  defp split_spec({:when, _, [spec, guard]}, default) do
    {spec, default, guard}
  end

  defp split_spec({:::, _, [spec, return]}, _default) do
    {spec, return, []}
  end

  defp split_spec(spec, default) do
    {spec, default, []}
  end

  defp do_defcallback(kind, {spec, return, guards}) do
    case Macro.decompose_call(spec) do
      {name, args} ->
        do_callback(kind, name, args, return, guards)
      _ ->
        raise ArgumentError, "invalid syntax in #{kind}callback #{Macro.to_string(spec)}"
    end
  end

  defp do_callback(kind, name, args, return, guards) do
    :lists.foreach fn
      {:::, _, [left, right]} ->
        ensure_not_default(left)
        ensure_not_default(right)
        left
      other ->
        ensure_not_default(other)
        other
    end, args

    spec =
      quote do
        unquote(name)(unquote_splicing(args)) :: unquote(return) when unquote(guards)
      end

    case kind do
      :def -> quote(do: @callback unquote(spec))
      :defmacro -> quote(do: @macrocallback unquote(spec))
    end
  end

  defp ensure_not_default({:\\, _, [_, _]}) do
    raise ArgumentError, "default arguments \\\\ not supported in defcallback/defmacrocallback"
  end

  defp ensure_not_default(_), do: :ok

  @doc false
  defmacro __using__(_) do
    quote do
      warning =
        "the Behaviour module is deprecated. Instead of using this module, " <>
        "use the @callback and @macrocallback module attributes. See the " <>
        "documentation for Module for more information on these attributes"
      IO.warn(warning)

      @doc false
      def __behaviour__(:callbacks) do
        __MODULE__.behaviour_info(:callbacks)
      end

      def __behaviour__(:docs) do
        for {tuple, line, kind, docs} <- Code.get_docs(__MODULE__, :callback_docs) do
          case kind do
            :callback -> {tuple, line, :def, docs}
            :macrocallback -> {tuple, line, :defmacro, docs}
          end
        end
      end

      import unquote(__MODULE__)
    end
  end
end


typoo

typoo

defmodule Eurexa.EurexaServer do
	@moduledoc """
	This is a gen server process, handling one Elixir service for Eureka. Each
	service requires one `EurexaServer`, which sends the regular heartbeats.

	The data sent to the Eureka Server has to comply to this XML schema and can
	be sent either as XML or as JSON data. We will use the JSON format.

	```lang:xml
	<?xml version="1.0" encoding="UTF-8"?>
	<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xsd:element name="instance">
        <xsd:complexType>
            <xsd:all>
                <!-- hostName in ec2 should be the public dns name, within ec2 public dns name will
                     always resolve to its private IP -->
                <xsd:element name="hostName" type="xsd:string" />
                <!-- app name
                     Instructions for adding a new app name -
                     <a _jive_internal="true" href="/clearspace/docs/DOC-20965"
                     target="_blank">http://wiki.netflix.com/clearspace/docs/DOC-20965</a> -->
                <xsd:element name="app" type="xsd:string" />
                <xsd:element name="ipAddr" type="xsd:string" />
                <xsd:element name="vipAddress" type="xsd:string" />
                <xsd:element name="secureVipAddress" type="xsd:string" />
                <xsd:element name="status" type="statusType" />
                <xsd:element name="port" type="xsd:positiveInteger" minOccurs="0" />
                <xsd:element name="securePort" type="xsd:positiveInteger" />
                <xsd:element name="homePageUrl" type="xsd:string" />
                <xsd:element name="statusPageUrl" type="xsd:string" />
                <xsd:element name="healthCheckUrl" type="xsd:string" />
               <xsd:element ref="dataCenterInfo" minOccurs="1" maxOccurs="1" />
                <!-- optional -->
                <xsd:element ref="leaseInfo" minOccurs="0"/>
                <!-- optional app specific metadata -->
                <xsd:element name="metadata" type="appMetadataType" minOccurs="0" />
            </xsd:all>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="dataCenterInfo">
        <xsd:complexType>
             <xsd:all>
                 <xsd:element name="name" type="dcNameType" />
                 <!-- metadata is only required if name is Amazon -->
                 <xsd:element name="metadata" type="amazonMetdataType" minOccurs="0"/>
             </xsd:all>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="leaseInfo">
        <xsd:complexType>
            <xsd:all>
                <!-- (optional) if you want to change the length of lease - default if 90 secs -->
                <xsd:element name="evictionDurationInSecs" minOccurs="0"
                type="xsd:positiveInteger"/>
            </xsd:all>
        </xsd:complexType>
    </xsd:element>

    <xsd:simpleType name="dcNameType">
        <!-- Restricting the values to a set of value using 'enumeration' -->
        <xsd:restriction base = "xsd:string">
            <xsd:enumeration value = "MyOwn"/>
            <xsd:enumeration value = "Amazon"/>
        </xsd:restriction>
    </xsd:simpleType>

    <xsd:simpleType name="statusType">
        <!-- Restricting the values to a set of value using 'enumeration' -->
        <xsd:restriction base = "xsd:string">
            <xsd:enumeration value = "UP"/>
            <xsd:enumeration value = "DOWN"/>
            <xsd:enumeration value = "STARTING"/>
            <xsd:enumeration value = "OUT_OF_SERVICE"/>
            <xsd:enumeration value = "UNKNOWN"/>
        </xsd:restriction>
    </xsd:simpleType>

    <xsd:complexType name="amazonMetdataType">
        <!-- From <a class="jive-link-external-small"
        href="http://docs.amazonwebservices.com/AWSEC2/latest/DeveloperGuide/index.html?AESDG-chapter-instancedata.html" target="_blank">http://docs.amazonwebservices.com/AWSEC2/latest/DeveloperGuide/index.html?AESDG-chapter-instancedata.html</a> -->
        <xsd:all>
            <xsd:element name="ami-launch-index" type="xsd:string" />
            <xsd:element name="local-hostname" type="xsd:string" />
            <xsd:element name="availability-zone" type="xsd:string" />
            <xsd:element name="instance-id" type="xsd:string" />
            <xsd:element name="public-ipv4" type="xsd:string" />
            <xsd:element name="public-hostname" type="xsd:string" />
            <xsd:element name="ami-manifest-path" type="xsd:string" />
            <xsd:element name="local-ipv4" type="xsd:string" />
            <xsd:element name="hostname" type="xsd:string"/>
            <xsd:element name="ami-id" type="xsd:string" />
            <xsd:element name="instance-type" type="xsd:string" />
        </xsd:all>
    </xsd:complexType>

    <xsd:complexType name="appMetadataType">
        <xsd:sequence>
            <!-- this is optional application specific name, value metadata -->
            <xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
        </xsd:sequence>
    </xsd:complexType>

	</xsd:schema>
	```
	"""

	@type status_t :: :UNKNOWN | :UP | :DOWN | :STARTING | :OUT_OF_SERVICE

	defstruct app: "my_app",
	  hostName: "localhost",
	  ipAddr: "127.0.0.1",
	  vipAddress: nil,
	  secureVipAddress: nil,
	  status: :UNKNOWN,
	  port: 0,
	  securePort: nil,
	  homePageUrl: nil,
	  statusPageUrl: nil,
	  healthCheckUrl: nil,
	  dataCenterInfo: %{
	  	name: "MyOwn",
	  	metadata: %{}
	  },
	  leaseInfo: %{ evictionDurationInSecs: 90},
	  metadata: %{}

	use GenServer

	@doc """
	Starts the Eurexa Server process for application `app_name`.
	"""
	def start_link(app_name) do
		GenServer.start_link(__MODULE__, [app_name], name: __MODULE__)
	end

	def init([app_name]) do
		app = %__MODULE__{app: app_name}
		timer = trigger_heartbeat(app)
		register(app)
		{:ok, {app, timer}}
	end

	def terminate(reason, {app, timer}) do
		:timer.cancel(timer)
		deregister(app.app, app.hostName)
	end

	@doc """
	Initializes the interval timer sending heartbeats to Eureka
	after 3/4 of the eviction interval, which usually 90 seconds.
	So, we are sending heatbeats every 67,5 seconds.
	"""
	def trigger_heartbeat(%__MODULE__{app: app_name, hostName: hostname,
			leaseInfo: %{evictionDurationInSecs: interval}}) do
		{:ok, tref} = :timer.apply_interval(interval * 750,
			__MODULE__, :send_heartbeat, [app_name, hostname])
		tref
	end

	def send_heartbeat(app_name, hostname) do

	end

	def deregister(app_name, hostname) do

	end

	def register(%__MODULE__{}) do

	end

end

defmodule Eurexa.EurexaServer do
	@moduledoc """
	This is a gen server process, handling one Elixir service for Eureka. Each
	service requires one `EurexaServer`, which sends the regular heartbeats.

	The data sent to the Eureka Server has to comply to this XML schema and can
	be sent either as XML or as JSON data. We will use the JSON format.

	```lang:xml
	<?xml version="1.0" encoding="UTF-8"?>
	<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xsd:element name="instance">
        <xsd:complexType>
            <xsd:all>
                <!-- hostName in ec2 should be the public dns name, within ec2 public dns name will
                     always resolve to its private IP -->
                <xsd:element name="hostName" type="xsd:string" />
                <!-- app name
                     Instructions for adding a new app name -
                     <a _jive_internal="true" href="/clearspace/docs/DOC-20965"
                     target="_blank">http://wiki.netflix.com/clearspace/docs/DOC-20965</a> -->
                <xsd:element name="app" type="xsd:string" />
                <xsd:element name="ipAddr" type="xsd:string" />
                <xsd:element name="vipAddress" type="xsd:string" />
                <xsd:element name="secureVipAddress" type="xsd:string" />
                <xsd:element name="status" type="statusType" />
                <xsd:element name="port" type="xsd:positiveInteger" minOccurs="0" />
                <xsd:element name="securePort" type="xsd:positiveInteger" />
                <xsd:element name="homePageUrl" type="xsd:string" />
                <xsd:element name="statusPageUrl" type="xsd:string" />
                <xsd:element name="healthCheckUrl" type="xsd:string" />
               <xsd:element ref="dataCenterInfo" minOccurs="1" maxOccurs="1" />
                <!-- optional -->
                <xsd:element ref="leaseInfo" minOccurs="0"/>
                <!-- optional app specific metadata -->
                <xsd:element name="metadata" type="appMetadataType" minOccurs="0" />
            </xsd:all>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="dataCenterInfo">
        <xsd:complexType>
             <xsd:all>
                 <xsd:element name="name" type="dcNameType" />
                 <!-- metadata is only required if name is Amazon -->
                 <xsd:element name="metadata" type="amazonMetdataType" minOccurs="0"/>
             </xsd:all>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="leaseInfo">
        <xsd:complexType>
            <xsd:all>
                <!-- (optional) if you want to change the length of lease - default if 90 secs -->
                <xsd:element name="evictionDurationInSecs" minOccurs="0"
                type="xsd:positiveInteger"/>
            </xsd:all>
        </xsd:complexType>
    </xsd:element>

    <xsd:simpleType name="dcNameType">
        <!-- Restricting the values to a set of value using 'enumeration' -->
        <xsd:restriction base = "xsd:string">
            <xsd:enumeration value = "MyOwn"/>
            <xsd:enumeration value = "Amazon"/>
        </xsd:restriction>
    </xsd:simpleType>

    <xsd:simpleType name="statusType">
        <!-- Restricting the values to a set of value using 'enumeration' -->
        <xsd:restriction base = "xsd:string">
            <xsd:enumeration value = "UP"/>
            <xsd:enumeration value = "DOWN"/>
            <xsd:enumeration value = "STARTING"/>
            <xsd:enumeration value = "OUT_OF_SERVICE"/>
            <xsd:enumeration value = "UNKNOWN"/>
        </xsd:restriction>
    </xsd:simpleType>

    <xsd:complexType name="amazonMetdataType">
        <!-- From <a class="jive-link-external-small"
        href="http://docs.amazonwebservices.com/AWSEC2/latest/DeveloperGuide/index.html?AESDG-chapter-instancedata.html" target="_blank">http://docs.amazonwebservices.com/AWSEC2/latest/DeveloperGuide/index.html?AESDG-chapter-instancedata.html</a> -->
        <xsd:all>
            <xsd:element name="ami-launch-index" type="xsd:string" />
            <xsd:element name="local-hostname" type="xsd:string" />
            <xsd:element name="availability-zone" type="xsd:string" />
            <xsd:element name="instance-id" type="xsd:string" />
            <xsd:element name="public-ipv4" type="xsd:string" />
            <xsd:element name="public-hostname" type="xsd:string" />
            <xsd:element name="ami-manifest-path" type="xsd:string" />
            <xsd:element name="local-ipv4" type="xsd:string" />
            <xsd:element name="hostname" type="xsd:string"/>
            <xsd:element name="ami-id" type="xsd:string" />
            <xsd:element name="instance-type" type="xsd:string" />
        </xsd:all>
    </xsd:complexType>

    <xsd:complexType name="appMetadataType">
        <xsd:sequence>
            <!-- this is optional application specific name, value metadata -->
            <xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
        </xsd:sequence>
    </xsd:complexType>

	</xsd:schema>
	```
	"""

	@type status.t = :UNKNOWN | :UP | :DOWN | :STARTING | :OUT_OF_SERVICE

	defstruct app: "my_app",
	  hostName: "localhost",
	  ipAddr: "127.0.0.1",
	  vipAddress: nil,
	  secureVipAddress: nil,
	  status: :UNKNOWN,
	  port: 0,
	  securePort: nil,
	  homePageUrl: nil,
	  statusPageUrl: nil,
	  healthCheckUrl: nil,
	  dataCenterInfo: %{
	  	name: "MyOwn",
	  	metadata: %{}
	  },
	  leaseInfo: %{ evictionDurationInSecs: 90},
	  metadata: %{}

	use GenServer

	@doc """
	Starts the Eurexa Server process for application `app_name`.
	"""
	def start_link(app_name) do
		GenServer.start_link(__MODULE__, [app_name], name: __MODULE__)
	end

	def init([app_name]) do
		app = %__MODULE__{app: app_name}
		timer = trigger_heartbeat(app)
		register(app)
		{:ok, {app, timer}}
	end

	def terminate(reason, {app, timer}) do
		:timer.cancel(timer)
		deregister(app.app, app.hostName)
	end

	@doc """
	Initializes the interval timer sending heartbeats to Eureka
	after 3/4 of the eviction interval, which usually 90 seconds.
	So, we are sending heatbeats every 67,5 seconds.
	"""
	def trigger_heartbeat(%__MODULE__{app: app_name, hostName: hostname,
			leaseInfo: %{evictionDurationInSecs: interval}}) do
		{:ok, tref} = :timer.apply_interval(interval * 750,
			__MODULE__, :send_heartbeat, [app_name, hostname])
		tref
	end

	def send_heartbeat(app_name, hostname) do

	end

	def deregister(app_name, hostname) do

	end

	def register(%__MODULE__{}) do

	end

end


URI decodes params embedded in route paths

Query params are already decoded by Plug. However params embedded in
paths by the router were not decoded

defmodule Phoenix.Router.Route do
  # This module defines the Route struct that is used
  # throughout Phoenix's router. This struct is private
  # as it contains internal routing information.
  @moduledoc false

  alias Phoenix.Router.Route

  @doc """
  The `Phoenix.Router.Route` struct. It stores:

    * :verb - the HTTP verb as an upcased string
    * :path - the normalized path as string
    * :host - the request host or host prefix
    * :controller - the controller module
    * :action - the action as an atom
    * :helper - the name of the helper as a string (may be nil)
    * :private - the private route info
    * :pipe_through - the pipeline names as a list of atoms

  """
  defstruct [:verb, :path, :host, :controller, :action,
             :helper, :private, :pipe_through]

  @type t :: %Route{}

  @doc """
  Receives the verb, path, controller, action and helper
  and returns a `Phoenix.Router.Route` struct.
  """
  @spec build(String.t, String.t, String.t | nil, atom, atom, atom | nil, atom, %{}) :: t
  def build(verb, path, host, controller, action, helper, pipe_through, private)
      when is_binary(verb) and is_binary(path) and (is_binary(host) or is_nil(host)) and
           is_atom(controller) and is_atom(action) and (is_binary(helper) or is_nil(helper)) and
           is_list(pipe_through) and is_map(private) do
    %Route{verb: verb, path: path, host: host, private: private,
           controller: controller, action: action, helper: helper,
           pipe_through: pipe_through}
  end

  @doc """
  Builds the expressions used by the route.
  """
  def exprs(route) do
    {path, binding} = build_path_and_binding(route.path)

    %{path: path,
      host: build_host(route.host),
      binding: binding,
      dispatch: build_dispatch(route, binding)}
  end

  defp build_path_and_binding(path) do
    {params, segments} = Plug.Router.Utils.build_path_match(path)

    binding = Enum.map(params, fn var ->
      {Atom.to_string(var), Macro.var(var, nil)}
    end)

    {segments, binding}
  end

  defp build_host(host) do
    cond do
      is_nil(host)             -> quote do: _
      String.last(host) == "." -> quote do: unquote(host) <> _
      true                     -> host
    end
  end

  defp build_dispatch(route, binding) do
    exprs =
      [maybe_binding(binding),
       maybe_merge(:private, route.private),
       build_pipes(route)]

    {:__block__, [], Enum.filter(exprs, & &1 != nil)}
  end

  defp maybe_merge(key, data) do
    if map_size(data) > 0 do
      quote do
        var!(conn) =
          update_in var!(conn).unquote(key), &Map.merge(&1, unquote(Macro.escape(data)))
      end
    end
  end

  defp maybe_binding([]), do: nil
  defp maybe_binding(binding) do
    quote do
      decoded_params = unquote({:%{}, [], binding})
         |> Enum.map(fn({k,v}) ->
           case v do
             <<_::binary>> -> {k, URI.decode(v)}
             _ -> {k, v}
           end
         end)
         |> Enum.into(%{})
      var!(conn) =
        update_in var!(conn).params, &Map.merge(&1, decoded_params)
    end
  end

  defp build_pipes(route) do
    initial = quote do
      var!(conn)
      |> Plug.Conn.put_private(:phoenix_pipelines, unquote(route.pipe_through))
      |> Plug.Conn.put_private(:phoenix_route, fn conn ->
           opts = unquote(route.controller).init(unquote(route.action))
           unquote(route.controller).call(conn, opts)
         end)
    end

    Enum.reduce(route.pipe_through, initial, &{&1, [], [&2, []]})
  end
end

defmodule Phoenix.Router.Route do
  # This module defines the Route struct that is used
  # throughout Phoenix's router. This struct is private
  # as it contains internal routing information.
  @moduledoc false

  alias Phoenix.Router.Route

  @doc """
  The `Phoenix.Router.Route` struct. It stores:

    * :verb - the HTTP verb as an upcased string
    * :path - the normalized path as string
    * :host - the request host or host prefix
    * :controller - the controller module
    * :action - the action as an atom
    * :helper - the name of the helper as a string (may be nil)
    * :private - the private route info
    * :pipe_through - the pipeline names as a list of atoms

  """
  defstruct [:verb, :path, :host, :controller, :action,
             :helper, :private, :pipe_through]

  @type t :: %Route{}

  @doc """
  Receives the verb, path, controller, action and helper
  and returns a `Phoenix.Router.Route` struct.
  """
  @spec build(String.t, String.t, String.t | nil, atom, atom, atom | nil, atom, %{}) :: t
  def build(verb, path, host, controller, action, helper, pipe_through, private)
      when is_binary(verb) and is_binary(path) and (is_binary(host) or is_nil(host)) and
           is_atom(controller) and is_atom(action) and (is_binary(helper) or is_nil(helper)) and
           is_list(pipe_through) and is_map(private) do
    %Route{verb: verb, path: path, host: host, private: private,
           controller: controller, action: action, helper: helper,
           pipe_through: pipe_through}
  end

  @doc """
  Builds the expressions used by the route.
  """
  def exprs(route) do
    {path, binding} = build_path_and_binding(route.path)

    %{path: path,
      host: build_host(route.host),
      binding: binding,
      dispatch: build_dispatch(route, binding)}
  end

  defp build_path_and_binding(path) do
    {params, segments} = Plug.Router.Utils.build_path_match(path)

    binding = Enum.map(params, fn var ->
      {Atom.to_string(var), Macro.var(var, nil)}
    end)

    {segments, binding}
  end

  defp build_host(host) do
    cond do
      is_nil(host)             -> quote do: _
      String.last(host) == "." -> quote do: unquote(host) <> _
      true                     -> host
    end
  end

  defp build_dispatch(route, binding) do
    exprs =
      [maybe_binding(binding),
       maybe_merge(:private, route.private),
       build_pipes(route)]

    {:__block__, [], Enum.filter(exprs, & &1 != nil)}
  end

  defp maybe_merge(key, data) do
    if map_size(data) > 0 do
      quote do
        var!(conn) =
          update_in var!(conn).unquote(key), &Map.merge(&1, unquote(Macro.escape(data)))
      end
    end
  end

  defp maybe_binding([]), do: nil
  defp maybe_binding(binding) do
    quote do
      var!(conn) =
        update_in var!(conn).params, &Map.merge(&1, unquote({:%{}, [], binding}))
    end
  end

  defp build_pipes(route) do
    initial = quote do
      var!(conn)
      |> Plug.Conn.put_private(:phoenix_pipelines, unquote(route.pipe_through))
      |> Plug.Conn.put_private(:phoenix_route, fn conn ->
           opts = unquote(route.controller).init(unquote(route.action))
           unquote(route.controller).call(conn, opts)
         end)
    end

    Enum.reduce(route.pipe_through, initial, &{&1, [], [&2, []]})
  end
end


Attempt to improve elixir version error message

defexception Mix.NoTaskError, task: nil, mix_error: true do
  def message(exception) do
    "The task #{task(exception)} could not be found"
  end
end

defexception Mix.InvalidTaskError, task: nil, mix_error: true do
  def message(exception) do
    "The task #{task(exception)} does not respond to run/1"
  end
end

defexception Mix.NoProjectError, mix_error: true,
  message: "Could not find a Mix.Project, please ensure a mix.exs file is available"

defexception Mix.Error, mix_error: true, message: nil

defexception Mix.ElixirVersionError, mix_error: true, target: nil, expected: nil, actual: nil do
  def message(exception) do
    "You're trying to run #{inspect exception.target} on Elixir v#{exception.actual} " <>
    "but it has declared in its mix.exs file it supports only Elixir #{exception.expected}"
  end
end

defexception Mix.NoTaskError, task: nil, mix_error: true do
  def message(exception) do
    "The task #{task(exception)} could not be found"
  end
end

defexception Mix.InvalidTaskError, task: nil, mix_error: true do
  def message(exception) do
    "The task #{task(exception)} does not respond to run/1"
  end
end

defexception Mix.NoProjectError, mix_error: true,
  message: "Could not find a Mix.Project, please ensure a mix.exs file is available"

defexception Mix.Error, mix_error: true, message: nil

defexception Mix.ElixirVersionError, mix_error: true, target: nil, expected: nil, actual: nil do
  def message(exception) do
    "You're trying to run #{inspect exception.target} on Elixir v#{exception.actual} " <>
    "but it's supposed to run on Elixir #{exception.expected}"
  end
end


Mix.Tasks.Test moduledoc example formatting (#8714)

defmodule Mix.Tasks.Test do
  defmodule Cover do
    @default_threshold 90

    @moduledoc false

    def start(compile_path, opts) do
      Mix.shell().info("Cover compiling modules ...")
      _ = :cover.stop()
      _ = :cover.start()

      case :cover.compile_beam_directory(compile_path |> to_charlist) do
        results when is_list(results) ->
          :ok

        {:error, _} ->
          Mix.raise("Failed to cover compile directory: " <> compile_path)
      end

      fn ->
        Mix.shell().info("\nGenerating cover results ...\n")
        {:result, ok, _fail} = :cover.analyse(:coverage, :line)

        {module_results, totals} = gather_coverage(ok, :cover.modules())
        module_results = Enum.sort_by(module_results, &percentage(elem(&1, 1)), &>=/2)

        if summary_opts = Keyword.get(opts, :summary, true) do
          console(module_results, totals, summary_opts)
        end

        html(module_results, opts)
      end
    end

    defp gather_coverage(results, keep) do
      # When gathering coverage results, we need to skip any
      # entry with line equal to 0 as those are generated code.
      #
      # We may also have multiple entries on the same line.
      # Each line is only considered once.
      #
      # We use ETS for performance, to avoid working with nested maps.
      table = :ets.new(__MODULE__, [:set, :private])

      try do
        Enum.each(results, fn
          {{module, 0}, _} -> :ets.insert(table, {{module, 0}, :dummy})
          {{module, line}, {1, 0}} -> :ets.insert(table, {{module, line}, true})
          {{module, line}, {0, 1}} -> :ets.insert_new(table, {{module, line}, false})
        end)

        module_results =
          for module <- keep,
              results = read_module_cover_results(table, module),
              do: {module, results}

        total_covered = :ets.select_count(table, [{{:_, true}, [], [true]}])
        total_not_covered = :ets.select_count(table, [{{:_, false}, [], [true]}])

        {module_results, {total_covered, total_not_covered}}
      after
        :ets.delete(table)
      end
    end

    defp read_module_cover_results(table, module) do
      covered = :ets.select_count(table, [{{{module, :_}, true}, [], [true]}])
      not_covered = :ets.select_count(table, [{{{module, :_}, false}, [], [true]}])
      {covered, not_covered}
    end

    defp console(results, totals, true), do: console(results, totals, [])

    defp console(results, totals, opts) when is_list(opts) do
      Mix.shell().info("Percentage | Module")
      Mix.shell().info("-----------|--------------------------")
      Enum.each(results, &display(&1, opts))
      Mix.shell().info("-----------|--------------------------")
      display({"Total", totals}, opts)
      Mix.shell().info("")
    end

    defp html(results, opts) do
      output = opts[:output]
      File.mkdir_p!(output)

      for {mod, _} <- results do
        {:ok, _} = :cover.analyse_to_file(mod, '#{output}/#{mod}.html', [:html])
      end

      Mix.shell().info([
        "Generated HTML coverage results in '",
        output,
        "' directory\n"
      ])
    end

    defp color(percentage, true), do: color(percentage, @default_threshold)
    defp color(_, false), do: ""
    defp color(percentage, threshold) when percentage > threshold, do: :green
    defp color(_, _), do: :red

    defp display({name, coverage}, opts) do
      threshold = Keyword.get(opts, :threshold, @default_threshold)
      percentage = percentage(coverage)

      Mix.shell().info([
        color(percentage, threshold),
        format(percentage, 9),
        "%",
        :reset,
        " | ",
        format_name(name)
      ])
    end

    defp percentage({0, 0}), do: 100.0
    defp percentage({covered, not_covered}), do: covered / (covered + not_covered) * 100

    defp format(number, length), do: :io_lib.format("~#{length}.2f", [number])

    defp format_name(name) when is_binary(name), do: name
    defp format_name(mod) when is_atom(mod), do: inspect(mod)
  end

  use Mix.Task

  alias Mix.Compilers.Test, as: CT

  @shortdoc "Runs a project's tests"
  @recursive true
  @preferred_cli_env :test

  @moduledoc """
  Runs the tests for a project.

  This task starts the current application, loads up
  `test/test_helper.exs` and then requires all files matching the
  `test/**/*_test.exs` pattern in parallel.

  A list of files can be given after the task name in order to select
  the files to compile:

      mix test test/some/particular/file_test.exs

  ## Command line options

    * `--color` - enables color in the output
    * `--cover` - runs coverage tool. See "Coverage" section below
    * `--exclude` - excludes tests that match the filter
    * `--failed` - runs only tests that failed the last time they ran
    * `--force` - forces compilation regardless of modification times
    * `--formatter` - formatter module
    * `--include` - includes tests that match the filter
    * `--listen-on-stdin` - runs tests, and then listens on stdin. Receiving a newline will
      result in the tests being run again. Very useful when combined with `--stale` and
      external commands which produce output on stdout upon file system modification
    * `--max-cases` - sets the maximum number of tests running async. Only tests from
      different modules run in parallel. Defaults to twice the number of cores
    * `--max-failures` - the suite stops evaluating tests when this number of test
      failures is reached. When not given, it always runs all tests
    * `--no-archives-check` - does not check archives
    * `--no-color` - disables color in the output
    * `--no-compile` - does not compile, even if files require compilation
    * `--no-deps-check` - does not check dependencies
    * `--no-elixir-version-check` - does not check the Elixir version from `mix.exs`
    * `--no-start` - does not start applications after compilation
    * `--only` - runs only tests that match the filter
    * `--preload-modules` - preloads all modules defined in applications
    * `--raise` - raises if the test suite failed
    * `--seed` - seeds the random number generator used to randomize the order of tests;
      `--seed 0` disables randomization
    * `--slowest` - prints timing information for the N slowest tests.
      Automatically sets `--trace` and `--preload-modules`
    * `--stale` - runs only tests which reference modules that changed since the
      last time tests were ran with `--stale`. You can read more about this option
      in the "Stale" section below
    * `--timeout` - sets the timeout for the tests
    * `--trace` - runs tests with detailed reporting. Automatically sets `--max-cases` to 1.
      Note that in trace mode test timeouts will be ignored

  See `ExUnit.configure/1` for more information on configuration options.

  ## Filters

  ExUnit provides tags and filtering functionality that allow developers
  to select which tests to run. The most common functionality is to exclude
  some particular tests from running by default in your test helper file:

      # Exclude all external tests from running
      ExUnit.configure(exclude: [external: true])

  Then, whenever desired, those tests could be included in the run via the
  `--include` option:

      mix test --include external:true

  The example above will run all tests that have the external option set to
  `true`. It is also possible to include all examples that have a given tag,
  regardless of its value:

      mix test --include external

  Note that all tests are included by default, so unless they are excluded
  first (either in the test helper or via the `--exclude` option) the
  `--include` option has no effect.

  For this reason, Mix also provides an `--only` option that excludes all
  tests and includes only the given ones:

      mix test --only external

  Which is similar to:

      mix test --include external --exclude test

  It differs in that the test suite will fail if no tests are executed when the `--only` option is used.

  In case a single file is being tested, it is possible to pass one or more specific
  line numbers to run only those given tests:

      mix test test/some/particular/file_test.exs:12

  Which is equivalent to:

      mix test --exclude test --include line:12 test/some/particular/file_test.exs

  Or:

      mix test test/some/particular/file_test.exs:12:24

  Which is equivalent to:

      mix test --exclude test --include line:12 --include line:24 test/some/particular/file_test.exs

  If a given line starts a `describe` block, that line filter runs all tests in it.
  Otherwise, it runs the closest test on or before the given line number.

  ## Configuration

    * `:test_paths` - list of paths containing test files. Defaults to
      `["test"]` if the `test` directory exists; otherwise, it defaults to `[]`.
      It is expected that all test paths contain a `test_helper.exs` file

    * `:test_pattern` - a pattern to load test files. Defaults to `*_test.exs`

    * `:warn_test_pattern` - a pattern to match potentially misnamed test files
      and display a warning. Defaults to `*_test.ex`

    * `:test_coverage` - a set of options to be passed down to the coverage
      mechanism

  ## Coverage

  The `:test_coverage` configuration accepts the following options:

    * `:output` - the output directory for cover results. Defaults to `"cover"`
    * `:tool` - the coverage tool
    * `:summary` - summary output configuration; can be either a boolean
      or a keyword list. When a keyword list is passed, it can specify a `:threshold`,
      which is a boolean or numeric value that enables coloring of code coverage
      results in red or green depending on whether the percentage is below or
      above the specified threshold, respectively. Defaults to `[threshold: 90]`

  By default, a very simple wrapper around OTP's `cover` is used as a tool,
  but it can be overridden as follows:

      def project() do
        [
          ...
          test_coverage: [tool: CoverModule]
          ...
        ]
      end

  `CoverModule` can be any module that exports `start/2`, receiving the
  compilation path and the `test_coverage` options as arguments.
  It must return either `nil` or an anonymous function of zero arity that will
  be run after the test suite is done.

  ## "Stale"

  The `--stale` command line option attempts to run only those test files which
  reference modules that have changed since the last time you ran this task with
  `--stale`.

  The first time this task is run with `--stale`, all tests are run and a manifest
  is generated. On subsequent runs, a test file is marked "stale" if any modules it
  references (and any modules those modules reference, recursively) were modified
  since the last run with `--stale`. A test file is also marked "stale" if it has
  been changed since the last run with `--stale`.
  """

  @switches [
    force: :boolean,
    color: :boolean,
    cover: :boolean,
    trace: :boolean,
    max_cases: :integer,
    max_failures: :integer,
    include: :keep,
    exclude: :keep,
    seed: :integer,
    only: :keep,
    compile: :boolean,
    start: :boolean,
    timeout: :integer,
    raise: :boolean,
    deps_check: :boolean,
    archives_check: :boolean,
    elixir_version_check: :boolean,
    failed: :boolean,
    stale: :boolean,
    listen_on_stdin: :boolean,
    formatter: :keep,
    slowest: :integer,
    preload_modules: :boolean
  ]

  @cover [output: "cover", tool: Cover]

  @impl true
  def run(args) do
    {opts, files} = OptionParser.parse!(args, strict: @switches)

    if opts[:listen_on_stdin] do
      System.at_exit(fn _ ->
        IO.gets(:stdio, "")
        Mix.shell().info("Restarting...")
        :init.restart()
        Process.sleep(:infinity)
      end)
    end

    unless System.get_env("MIX_ENV") || Mix.env() == :test do
      Mix.raise(
        "\"mix test\" is running in the \"#{Mix.env()}\" environment. If you are " <>
          "running tests alongside another task, please set MIX_ENV explicitly"
      )
    end

    Mix.Task.run("loadpaths", args)

    if Keyword.get(opts, :compile, true) do
      Mix.Project.compile(args)
    end

    project = Mix.Project.config()

    # Start cover after we load deps but before we start the app.
    cover =
      if opts[:cover] do
        compile_path = Mix.Project.compile_path(project)
        cover = Keyword.merge(@cover, project[:test_coverage] || [])
        cover[:tool].start(compile_path, cover)
      end

    # Start the app and configure ExUnit with command line options
    # before requiring test_helper.exs so that the configuration is
    # available in test_helper.exs
    Mix.shell().print_app
    app_start_args = if opts[:slowest], do: ["--preload-modules" | args], else: args
    Mix.Task.run("app.start", app_start_args)

    # Ensure ExUnit is loaded.
    case Application.load(:ex_unit) do
      :ok -> :ok
      {:error, {:already_loaded, :ex_unit}} -> :ok
    end

    # Then configure ExUnit again so that command line options
    # override test_helper.exs
    {ex_unit_opts, allowed_files} = process_ex_unit_opts(opts)
    ExUnit.configure(ex_unit_opts)

    test_paths = project[:test_paths] || default_test_paths()
    Enum.each(test_paths, &require_test_helper(&1))
    ExUnit.configure(merge_helper_opts(ex_unit_opts))

    # Finally parse, require and load the files
    test_files = parse_files(files, test_paths)
    test_pattern = project[:test_pattern] || "*_test.exs"
    warn_test_pattern = project[:warn_test_pattern] || "*_test.ex"

    matched_test_files =
      test_files
      |> Mix.Utils.extract_files(test_pattern)
      |> filter_to_allowed_files(allowed_files)

    display_warn_test_pattern(test_files, test_pattern, matched_test_files, warn_test_pattern)

    case CT.require_and_run(matched_test_files, test_paths, opts) do
      {:ok, %{excluded: excluded, failures: failures, total: total}} ->
        cover && cover.()

        option_only_present? = Keyword.has_key?(opts, :only)

        cond do
          failures > 0 and opts[:raise] ->
            Mix.raise("\"mix test\" failed")

          failures > 0 ->
            System.at_exit(fn _ -> exit({:shutdown, 1}) end)

          excluded == total and option_only_present? ->
            message = "The --only option was given to \"mix test\" but no test was executed"
            raise_or_error_at_exit(message, opts)

          true ->
            :ok
        end

      :noop ->
        cond do
          opts[:stale] ->
            Mix.shell().info("No stale tests")

          files == [] ->
            Mix.shell().info("There are no tests to run")

          true ->
            message = "Paths given to \"mix test\" did not match any directory/file: "
            raise_or_error_at_exit(message <> Enum.join(files, ", "), opts)
        end

        :ok
    end
  end

  defp raise_or_error_at_exit(message, opts) do
    cond do
      opts[:raise] ->
        Mix.raise(message)

      Mix.Task.recursing?() ->
        Mix.shell().info(message)

      true ->
        Mix.shell().error(message)
        System.at_exit(fn _ -> exit({:shutdown, 1}) end)
    end
  end

  defp display_warn_test_pattern(test_files, test_pattern, matched_test_files, warn_test_pattern) do
    files = Mix.Utils.extract_files(test_files, warn_test_pattern) -- matched_test_files

    for file <- files do
      Mix.shell().info(
        "warning: #{file} does not match #{inspect(test_pattern)} and won't be loaded"
      )
    end
  end

  @option_keys [
    :trace,
    :max_cases,
    :max_failures,
    :include,
    :exclude,
    :seed,
    :timeout,
    :formatters,
    :colors,
    :slowest,
    :failures_manifest_file,
    :only_test_ids
  ]

  @doc false
  def process_ex_unit_opts(opts) do
    {opts, allowed_files} =
      opts
      |> manifest_opts()
      |> failed_opts()

    opts =
      opts
      |> filter_opts(:include)
      |> filter_opts(:exclude)
      |> filter_opts(:only)
      |> formatter_opts()
      |> color_opts()
      |> Keyword.take(@option_keys)
      |> default_opts()

    {opts, allowed_files}
  end

  defp merge_helper_opts(opts) do
    merge_opts(opts, :exclude)
  end

  defp default_opts(opts) do
    # Set autorun to false because Mix
    # automatically runs the test suite for us.
    [autorun: false] ++ opts
  end

  defp parse_files([], test_paths) do
    test_paths
  end

  defp parse_files([single_file], _test_paths) do
    # Check if the single file path matches test/path/to_test.exs:123. If it does,
    # apply "--only line:123" and trim the trailing :123 part.
    {single_file, opts} = ExUnit.Filters.parse_path(single_file)
    ExUnit.configure(opts)
    [single_file]
  end

  defp parse_files(files, _test_paths) do
    if Enum.any?(files, &match?({_, [_ | _]}, ExUnit.Filters.parse_path(&1))) do
      Mix.raise("Line numbers can only be used when running a single test file")
    else
      files
    end
  end

  defp parse_filters(opts, key) do
    if Keyword.has_key?(opts, key) do
      ExUnit.Filters.parse(Keyword.get_values(opts, key))
    end
  end

  defp filter_opts(opts, :only) do
    if filters = parse_filters(opts, :only) do
      opts
      |> Keyword.update(:include, filters, &(filters ++ &1))
      |> Keyword.update(:exclude, [:test], &[:test | &1])
    else
      opts
    end
  end

  defp filter_opts(opts, key) do
    if filters = parse_filters(opts, key) do
      Keyword.put(opts, key, filters)
    else
      opts
    end
  end

  defp formatter_opts(opts) do
    if Keyword.has_key?(opts, :formatter) do
      formatters =
        opts
        |> Keyword.get_values(:formatter)
        |> Enum.map(&Module.concat([&1]))

      Keyword.put(opts, :formatters, formatters)
    else
      opts
    end
  end

  @manifest_file_name ".mix_test_failures"

  defp manifest_opts(opts) do
    manifest_file = Path.join(Mix.Project.manifest_path(), @manifest_file_name)
    Keyword.put(opts, :failures_manifest_file, manifest_file)
  end

  defp failed_opts(opts) do
    if opts[:failed] do
      if opts[:stale] do
        Mix.raise("Combining --failed and --stale is not supported.")
      end

      {allowed_files, failed_ids} = ExUnit.Filters.failure_info(opts[:failures_manifest_file])
      {Keyword.put(opts, :only_test_ids, failed_ids), allowed_files}
    else
      {opts, nil}
    end
  end

  defp filter_to_allowed_files(matched_test_files, nil), do: matched_test_files

  defp filter_to_allowed_files(matched_test_files, %MapSet{} = allowed_files) do
    Enum.filter(matched_test_files, &MapSet.member?(allowed_files, Path.expand(&1)))
  end

  defp color_opts(opts) do
    case Keyword.fetch(opts, :color) do
      {:ok, enabled?} ->
        Keyword.put(opts, :colors, enabled: enabled?)

      :error ->
        opts
    end
  end

  defp merge_opts(opts, key) do
    value = List.wrap(Application.get_env(:ex_unit, key, []))
    Keyword.update(opts, key, value, &Enum.uniq(&1 ++ value))
  end

  defp require_test_helper(dir) do
    file = Path.join(dir, "test_helper.exs")

    if File.exists?(file) do
      Code.require_file(file)
    else
      Mix.raise("Cannot run tests because test helper file #{inspect(file)} does not exist")
    end
  end

  defp default_test_paths do
    if File.dir?("test") do
      ["test"]
    else
      []
    end
  end
end

defmodule Mix.Tasks.Test do
  defmodule Cover do
    @default_threshold 90

    @moduledoc false

    def start(compile_path, opts) do
      Mix.shell().info("Cover compiling modules ...")
      _ = :cover.stop()
      _ = :cover.start()

      case :cover.compile_beam_directory(compile_path |> to_charlist) do
        results when is_list(results) ->
          :ok

        {:error, _} ->
          Mix.raise("Failed to cover compile directory: " <> compile_path)
      end

      fn ->
        Mix.shell().info("\nGenerating cover results ...\n")
        {:result, ok, _fail} = :cover.analyse(:coverage, :line)

        {module_results, totals} = gather_coverage(ok, :cover.modules())
        module_results = Enum.sort_by(module_results, &percentage(elem(&1, 1)), &>=/2)

        if summary_opts = Keyword.get(opts, :summary, true) do
          console(module_results, totals, summary_opts)
        end

        html(module_results, opts)
      end
    end

    defp gather_coverage(results, keep) do
      # When gathering coverage results, we need to skip any
      # entry with line equal to 0 as those are generated code.
      #
      # We may also have multiple entries on the same line.
      # Each line is only considered once.
      #
      # We use ETS for performance, to avoid working with nested maps.
      table = :ets.new(__MODULE__, [:set, :private])

      try do
        Enum.each(results, fn
          {{module, 0}, _} -> :ets.insert(table, {{module, 0}, :dummy})
          {{module, line}, {1, 0}} -> :ets.insert(table, {{module, line}, true})
          {{module, line}, {0, 1}} -> :ets.insert_new(table, {{module, line}, false})
        end)

        module_results =
          for module <- keep,
              results = read_module_cover_results(table, module),
              do: {module, results}

        total_covered = :ets.select_count(table, [{{:_, true}, [], [true]}])
        total_not_covered = :ets.select_count(table, [{{:_, false}, [], [true]}])

        {module_results, {total_covered, total_not_covered}}
      after
        :ets.delete(table)
      end
    end

    defp read_module_cover_results(table, module) do
      covered = :ets.select_count(table, [{{{module, :_}, true}, [], [true]}])
      not_covered = :ets.select_count(table, [{{{module, :_}, false}, [], [true]}])
      {covered, not_covered}
    end

    defp console(results, totals, true), do: console(results, totals, [])

    defp console(results, totals, opts) when is_list(opts) do
      Mix.shell().info("Percentage | Module")
      Mix.shell().info("-----------|--------------------------")
      Enum.each(results, &display(&1, opts))
      Mix.shell().info("-----------|--------------------------")
      display({"Total", totals}, opts)
      Mix.shell().info("")
    end

    defp html(results, opts) do
      output = opts[:output]
      File.mkdir_p!(output)

      for {mod, _} <- results do
        {:ok, _} = :cover.analyse_to_file(mod, '#{output}/#{mod}.html', [:html])
      end

      Mix.shell().info([
        "Generated HTML coverage results in '",
        output,
        "' directory\n"
      ])
    end

    defp color(percentage, true), do: color(percentage, @default_threshold)
    defp color(_, false), do: ""
    defp color(percentage, threshold) when percentage > threshold, do: :green
    defp color(_, _), do: :red

    defp display({name, coverage}, opts) do
      threshold = Keyword.get(opts, :threshold, @default_threshold)
      percentage = percentage(coverage)

      Mix.shell().info([
        color(percentage, threshold),
        format(percentage, 9),
        "%",
        :reset,
        " | ",
        format_name(name)
      ])
    end

    defp percentage({0, 0}), do: 100.0
    defp percentage({covered, not_covered}), do: covered / (covered + not_covered) * 100

    defp format(number, length), do: :io_lib.format("~#{length}.2f", [number])

    defp format_name(name) when is_binary(name), do: name
    defp format_name(mod) when is_atom(mod), do: inspect(mod)
  end

  use Mix.Task

  alias Mix.Compilers.Test, as: CT

  @shortdoc "Runs a project's tests"
  @recursive true
  @preferred_cli_env :test

  @moduledoc """
  Runs the tests for a project.

  This task starts the current application, loads up
  `test/test_helper.exs` and then requires all files matching the
  `test/**/*_test.exs` pattern in parallel.

  A list of files can be given after the task name in order to select
  the files to compile:

      mix test test/some/particular/file_test.exs

  ## Command line options

    * `--color` - enables color in the output
    * `--cover` - runs coverage tool. See "Coverage" section below
    * `--exclude` - excludes tests that match the filter
    * `--failed` - runs only tests that failed the last time they ran
    * `--force` - forces compilation regardless of modification times
    * `--formatter` - formatter module
    * `--include` - includes tests that match the filter
    * `--listen-on-stdin` - runs tests, and then listens on stdin. Receiving a newline will
      result in the tests being run again. Very useful when combined with `--stale` and
      external commands which produce output on stdout upon file system modification
    * `--max-cases` - sets the maximum number of tests running async. Only tests from
      different modules run in parallel. Defaults to twice the number of cores
    * `--max-failures` - the suite stops evaluating tests when this number of test
      failures is reached. When not given, it always runs all tests
    * `--no-archives-check` - does not check archives
    * `--no-color` - disables color in the output
    * `--no-compile` - does not compile, even if files require compilation
    * `--no-deps-check` - does not check dependencies
    * `--no-elixir-version-check` - does not check the Elixir version from `mix.exs`
    * `--no-start` - does not start applications after compilation
    * `--only` - runs only tests that match the filter
    * `--preload-modules` - preloads all modules defined in applications
    * `--raise` - raises if the test suite failed
    * `--seed` - seeds the random number generator used to randomize the order of tests;
      `--seed 0` disables randomization
    * `--slowest` - prints timing information for the N slowest tests.
      Automatically sets `--trace` and `--preload-modules`
    * `--stale` - runs only tests which reference modules that changed since the
      last time tests were ran with `--stale`. You can read more about this option
      in the "Stale" section below
    * `--timeout` - sets the timeout for the tests
    * `--trace` - runs tests with detailed reporting. Automatically sets `--max-cases` to 1.
      Note that in trace mode test timeouts will be ignored

  See `ExUnit.configure/1` for more information on configuration options.

  ## Filters

  ExUnit provides tags and filtering functionality that allow developers
  to select which tests to run. The most common functionality is to exclude
  some particular tests from running by default in your test helper file:

      # Exclude all external tests from running
      ExUnit.configure exclude: [external: true]

  Then, whenever desired, those tests could be included in the run via the
  `--include` option:

      mix test --include external:true

  The example above will run all tests that have the external option set to
  `true`. It is also possible to include all examples that have a given tag,
  regardless of its value:

      mix test --include external

  Note that all tests are included by default, so unless they are excluded
  first (either in the test helper or via the `--exclude` option) the
  `--include` option has no effect.

  For this reason, Mix also provides an `--only` option that excludes all
  tests and includes only the given ones:

      mix test --only external

  Which is similar to:

      mix test --include external --exclude test

  It differs in that the test suite will fail if no tests are executed when the `--only` option is used.

  In case a single file is being tested, it is possible to pass one or more specific
  line numbers to run only those given tests:

      mix test test/some/particular/file_test.exs:12

  Which is equivalent to:

      mix test --exclude test --include line:12 test/some/particular/file_test.exs

  Or:

      mix test test/some/particular/file_test.exs:12:24

  Which is equivalent to:

      mix test --exclude test --include line:12 --include line:24 test/some/particular/file_test.exs

  If a given line starts a `describe` block, that line filter runs all tests in it.
  Otherwise, it runs the closest test on or before the given line number.

  ## Configuration

    * `:test_paths` - list of paths containing test files. Defaults to
      `["test"]` if the `test` directory exists; otherwise, it defaults to `[]`.
      It is expected that all test paths contain a `test_helper.exs` file

    * `:test_pattern` - a pattern to load test files. Defaults to `*_test.exs`

    * `:warn_test_pattern` - a pattern to match potentially misnamed test files
      and display a warning. Defaults to `*_test.ex`

    * `:test_coverage` - a set of options to be passed down to the coverage
      mechanism

  ## Coverage

  The `:test_coverage` configuration accepts the following options:

    * `:output` - the output directory for cover results. Defaults to `"cover"`
    * `:tool` - the coverage tool
    * `:summary` - summary output configuration; can be either a boolean
      or a keyword list. When a keyword list is passed, it can specify a `:threshold`,
      which is a boolean or numeric value that enables coloring of code coverage
      results in red or green depending on whether the percentage is below or
      above the specified threshold, respectively. Defaults to `[threshold: 90]`

  By default, a very simple wrapper around OTP's `cover` is used as a tool,
  but it can be overridden as follows:

      def project() do
        [
          ...
          test_coverage: [tool: CoverModule]
          ...
        ]
      end

  `CoverModule` can be any module that exports `start/2`, receiving the
  compilation path and the `test_coverage` options as arguments.
  It must return either `nil` or an anonymous function of zero arity that will
  be run after the test suite is done.

  ## "Stale"

  The `--stale` command line option attempts to run only those test files which
  reference modules that have changed since the last time you ran this task with
  `--stale`.

  The first time this task is run with `--stale`, all tests are run and a manifest
  is generated. On subsequent runs, a test file is marked "stale" if any modules it
  references (and any modules those modules reference, recursively) were modified
  since the last run with `--stale`. A test file is also marked "stale" if it has
  been changed since the last run with `--stale`.
  """

  @switches [
    force: :boolean,
    color: :boolean,
    cover: :boolean,
    trace: :boolean,
    max_cases: :integer,
    max_failures: :integer,
    include: :keep,
    exclude: :keep,
    seed: :integer,
    only: :keep,
    compile: :boolean,
    start: :boolean,
    timeout: :integer,
    raise: :boolean,
    deps_check: :boolean,
    archives_check: :boolean,
    elixir_version_check: :boolean,
    failed: :boolean,
    stale: :boolean,
    listen_on_stdin: :boolean,
    formatter: :keep,
    slowest: :integer,
    preload_modules: :boolean
  ]

  @cover [output: "cover", tool: Cover]

  @impl true
  def run(args) do
    {opts, files} = OptionParser.parse!(args, strict: @switches)

    if opts[:listen_on_stdin] do
      System.at_exit(fn _ ->
        IO.gets(:stdio, "")
        Mix.shell().info("Restarting...")
        :init.restart()
        Process.sleep(:infinity)
      end)
    end

    unless System.get_env("MIX_ENV") || Mix.env() == :test do
      Mix.raise(
        "\"mix test\" is running in the \"#{Mix.env()}\" environment. If you are " <>
          "running tests alongside another task, please set MIX_ENV explicitly"
      )
    end

    Mix.Task.run("loadpaths", args)

    if Keyword.get(opts, :compile, true) do
      Mix.Project.compile(args)
    end

    project = Mix.Project.config()

    # Start cover after we load deps but before we start the app.
    cover =
      if opts[:cover] do
        compile_path = Mix.Project.compile_path(project)
        cover = Keyword.merge(@cover, project[:test_coverage] || [])
        cover[:tool].start(compile_path, cover)
      end

    # Start the app and configure ExUnit with command line options
    # before requiring test_helper.exs so that the configuration is
    # available in test_helper.exs
    Mix.shell().print_app
    app_start_args = if opts[:slowest], do: ["--preload-modules" | args], else: args
    Mix.Task.run("app.start", app_start_args)

    # Ensure ExUnit is loaded.
    case Application.load(:ex_unit) do
      :ok -> :ok
      {:error, {:already_loaded, :ex_unit}} -> :ok
    end

    # Then configure ExUnit again so that command line options
    # override test_helper.exs
    {ex_unit_opts, allowed_files} = process_ex_unit_opts(opts)
    ExUnit.configure(ex_unit_opts)

    test_paths = project[:test_paths] || default_test_paths()
    Enum.each(test_paths, &require_test_helper(&1))
    ExUnit.configure(merge_helper_opts(ex_unit_opts))

    # Finally parse, require and load the files
    test_files = parse_files(files, test_paths)
    test_pattern = project[:test_pattern] || "*_test.exs"
    warn_test_pattern = project[:warn_test_pattern] || "*_test.ex"

    matched_test_files =
      test_files
      |> Mix.Utils.extract_files(test_pattern)
      |> filter_to_allowed_files(allowed_files)

    display_warn_test_pattern(test_files, test_pattern, matched_test_files, warn_test_pattern)

    case CT.require_and_run(matched_test_files, test_paths, opts) do
      {:ok, %{excluded: excluded, failures: failures, total: total}} ->
        cover && cover.()

        option_only_present? = Keyword.has_key?(opts, :only)

        cond do
          failures > 0 and opts[:raise] ->
            Mix.raise("\"mix test\" failed")

          failures > 0 ->
            System.at_exit(fn _ -> exit({:shutdown, 1}) end)

          excluded == total and option_only_present? ->
            message = "The --only option was given to \"mix test\" but no test was executed"
            raise_or_error_at_exit(message, opts)

          true ->
            :ok
        end

      :noop ->
        cond do
          opts[:stale] ->
            Mix.shell().info("No stale tests")

          files == [] ->
            Mix.shell().info("There are no tests to run")

          true ->
            message = "Paths given to \"mix test\" did not match any directory/file: "
            raise_or_error_at_exit(message <> Enum.join(files, ", "), opts)
        end

        :ok
    end
  end

  defp raise_or_error_at_exit(message, opts) do
    cond do
      opts[:raise] ->
        Mix.raise(message)

      Mix.Task.recursing?() ->
        Mix.shell().info(message)

      true ->
        Mix.shell().error(message)
        System.at_exit(fn _ -> exit({:shutdown, 1}) end)
    end
  end

  defp display_warn_test_pattern(test_files, test_pattern, matched_test_files, warn_test_pattern) do
    files = Mix.Utils.extract_files(test_files, warn_test_pattern) -- matched_test_files

    for file <- files do
      Mix.shell().info(
        "warning: #{file} does not match #{inspect(test_pattern)} and won't be loaded"
      )
    end
  end

  @option_keys [
    :trace,
    :max_cases,
    :max_failures,
    :include,
    :exclude,
    :seed,
    :timeout,
    :formatters,
    :colors,
    :slowest,
    :failures_manifest_file,
    :only_test_ids
  ]

  @doc false
  def process_ex_unit_opts(opts) do
    {opts, allowed_files} =
      opts
      |> manifest_opts()
      |> failed_opts()

    opts =
      opts
      |> filter_opts(:include)
      |> filter_opts(:exclude)
      |> filter_opts(:only)
      |> formatter_opts()
      |> color_opts()
      |> Keyword.take(@option_keys)
      |> default_opts()

    {opts, allowed_files}
  end

  defp merge_helper_opts(opts) do
    merge_opts(opts, :exclude)
  end

  defp default_opts(opts) do
    # Set autorun to false because Mix
    # automatically runs the test suite for us.
    [autorun: false] ++ opts
  end

  defp parse_files([], test_paths) do
    test_paths
  end

  defp parse_files([single_file], _test_paths) do
    # Check if the single file path matches test/path/to_test.exs:123. If it does,
    # apply "--only line:123" and trim the trailing :123 part.
    {single_file, opts} = ExUnit.Filters.parse_path(single_file)
    ExUnit.configure(opts)
    [single_file]
  end

  defp parse_files(files, _test_paths) do
    if Enum.any?(files, &match?({_, [_ | _]}, ExUnit.Filters.parse_path(&1))) do
      Mix.raise("Line numbers can only be used when running a single test file")
    else
      files
    end
  end

  defp parse_filters(opts, key) do
    if Keyword.has_key?(opts, key) do
      ExUnit.Filters.parse(Keyword.get_values(opts, key))
    end
  end

  defp filter_opts(opts, :only) do
    if filters = parse_filters(opts, :only) do
      opts
      |> Keyword.update(:include, filters, &(filters ++ &1))
      |> Keyword.update(:exclude, [:test], &[:test | &1])
    else
      opts
    end
  end

  defp filter_opts(opts, key) do
    if filters = parse_filters(opts, key) do
      Keyword.put(opts, key, filters)
    else
      opts
    end
  end

  defp formatter_opts(opts) do
    if Keyword.has_key?(opts, :formatter) do
      formatters =
        opts
        |> Keyword.get_values(:formatter)
        |> Enum.map(&Module.concat([&1]))

      Keyword.put(opts, :formatters, formatters)
    else
      opts
    end
  end

  @manifest_file_name ".mix_test_failures"

  defp manifest_opts(opts) do
    manifest_file = Path.join(Mix.Project.manifest_path(), @manifest_file_name)
    Keyword.put(opts, :failures_manifest_file, manifest_file)
  end

  defp failed_opts(opts) do
    if opts[:failed] do
      if opts[:stale] do
        Mix.raise("Combining --failed and --stale is not supported.")
      end

      {allowed_files, failed_ids} = ExUnit.Filters.failure_info(opts[:failures_manifest_file])
      {Keyword.put(opts, :only_test_ids, failed_ids), allowed_files}
    else
      {opts, nil}
    end
  end

  defp filter_to_allowed_files(matched_test_files, nil), do: matched_test_files

  defp filter_to_allowed_files(matched_test_files, %MapSet{} = allowed_files) do
    Enum.filter(matched_test_files, &MapSet.member?(allowed_files, Path.expand(&1)))
  end

  defp color_opts(opts) do
    case Keyword.fetch(opts, :color) do
      {:ok, enabled?} ->
        Keyword.put(opts, :colors, enabled: enabled?)

      :error ->
        opts
    end
  end

  defp merge_opts(opts, key) do
    value = List.wrap(Application.get_env(:ex_unit, key, []))
    Keyword.update(opts, key, value, &Enum.uniq(&1 ++ value))
  end

  defp require_test_helper(dir) do
    file = Path.join(dir, "test_helper.exs")

    if File.exists?(file) do
      Code.require_file(file)
    else
      Mix.raise("Cannot run tests because test helper file #{inspect(file)} does not exist")
    end
  end

  defp default_test_paths do
    if File.dir?("test") do
      ["test"]
    else
      []
    end
  end
end


Fix issues with insecure_mkdir_temp

Fix issues with insecure_mkdir_temp

defmodule Mix.Releases.Utils do
  @moduledoc false
  alias Mix.Releases.{Logger, Release, App}

  @doc """
  Loads a template from :distillery's `priv/templates` directory based on the provided name.
  Any parameters provided are configured as bindings for the template

  ## Example

      iex> {:ok, contents} = #{__MODULE__}.template("vm.args", [rel_name: :test])
      ...> String.contains?(contents, "-name test@127.0.0.1")
      true
  """
  @spec template(atom | String.t, Keyword.t) :: {:ok, String.t} | {:error, String.t}
  def template(name, params \\ []) do
    try do
      template_path = Path.join(["#{:code.priv_dir(:distillery)}", "templates", "#{name}.eex"])
      {:ok, EEx.eval_file(template_path, params)}
    rescue
      e ->
        {:error, e.__struct__.message(e)}
    end
  end

  @doc """
  Writes an Elixir/Erlang term to the provided path
  """
  @spec write_term(String.t, term) :: :ok | {:error, term}
  def write_term(path, term) do
    :file.write_file('#{path}', :io_lib.fwrite('~p.\n', [term]), [encoding: :utf8])
  end

  @doc """
  Writes a collection of Elixir/Erlang terms to the provided path
  """
  @spec write_terms(String.t, [term]) :: :ok | {:error, term}
  def write_terms(path, terms) when is_list(terms) do
    contents = String.duplicate("~p.\n\n", Enum.count(terms))
       |> String.to_char_list
       |> :io_lib.fwrite(Enum.reverse(terms))
    :file.write_file('#{path}', contents, [encoding: :utf8])
  end

  @doc """
  Reads a file as Erlang terms
  """
  @spec read_terms(String.t) :: {:ok, [term]} :: {:error, String.t}
  def read_terms(path) do
    case :file.consult(String.to_charlist(path)) do
      {:ok, _} = result ->
        result
      {:error, {line, type, msg}} ->
        {:error, "Parse failed - #{path}@#{line} (#{type}): #{msg}"}
      {:error, reason} ->
        {:error, "Unable to access #{path} (#{reason})"}
    end
  end

  @doc """
  Determines the current ERTS version
  """
  @spec erts_version() :: String.t
  def erts_version, do: "#{:erlang.system_info(:version)}"

  @doc """
  Creates a temporary directory with a random name in a canonical
  temporary files directory of the current system
  (i.e. `/tmp` on *NIX or `./tmp` on Windows)

  Returns an ok tuple with the path of the temp directory, or an error
  tuple with the reason it failed.
  """
  @spec insecure_mkdir_temp() :: {:ok, String.t} | {:error, term}
  def insecure_mkdir_temp() do
    :rand.seed(:exs64)
    unique_num = :rand.uniform(1_000_000_000)
    tmpdir_path = case :erlang.system_info(:system_architecture) do
                    'win32' ->
                      Path.join(["./tmp", ".tmp_dir#{unique_num}"])
                    _ ->
                      Path.join(["/tmp", ".tmp_dir#{unique_num}"])
                  end
    case File.mkdir_p(tmpdir_path) do
      :ok -> {:ok, tmpdir_path}
      {:error, _} = err -> err
    end
  end

  @doc """
  Given a path to a release output directory, return a list
  of release versions that are present.

  ## Example

      iex> app_dir = Path.join([File.cwd!, "test", "fixtures", "mock_app"])
      ...> output_dir = Path.join([app_dir, "rel", "mock_app"])
      ...> #{__MODULE__}.get_release_versions(output_dir)
      ["0.2.2", "0.2.1-1-d3adb3f", "0.2.1", "0.2.0", "0.1.0"]
  """
  @spec get_release_versions(String.t) :: list(String.t)
  def get_release_versions(output_dir) do
    releases_path = Path.join([output_dir, "releases"])
    case File.exists?(releases_path) do
      false -> []
      true  ->
        releases_path
        |> File.ls!
        |> Enum.reject(fn entry -> entry in ["RELEASES", "start_erl.data"] end)
        |> sort_versions
    end
  end

  @git_describe_pattern ~r/(?<ver>\d+\.\d+\.\d+)-(?<commits>\d+)-(?<sha>[A-Ga-g0-9]+)/
  @doc """
  Sort a list of version strings, in reverse order (i.e. latest version comes first)
  Tries to use semver version compare, but can fall back to regular string compare.
  It also parses git-describe generated version strings and handles ordering them
  correctly.

  ## Example

      iex> #{__MODULE__}.sort_versions(["1.0.2", "1.0.1", "1.0.9", "1.0.10"])
      ["1.0.10", "1.0.9", "1.0.2", "1.0.1"]

      iex> #{__MODULE__}.sort_versions(["0.0.1", "0.0.2", "0.0.1-2-a1d2g3f", "0.0.1-1-deadbeef"])
      ["0.0.2", "0.0.1-2-a1d2g3f", "0.0.1-1-deadbeef", "0.0.1"]
  """
  @spec sort_versions(list(String.t)) :: list(String.t)
  def sort_versions(versions) do
    versions
    |> Enum.map(fn ver ->
        # Special handling for git-describe versions
        compared = case Regex.named_captures(@git_describe_pattern, ver) do
          nil ->
            {:standard, ver, nil}
          %{"ver" => version, "commits" => n, "sha" => sha} ->
            {:describe, <<version::binary, ?+, n::binary, ?-, sha::binary>>, String.to_integer(n)}
        end
        {ver, compared}
      end)
    |> Enum.sort(
      fn {_, {v1type, v1str, v1_commits_since}}, {_, {v2type, v2str, v2_commits_since}} ->
        case { parse_version(v1str), parse_version(v2str) } do
          {{:semantic, v1}, {:semantic, v2}} ->
            case Version.compare(v1, v2) do
              :gt -> true
              :eq ->
                case {v1type, v2type} do
                  {:standard, :standard} -> v1 > v2 # probably always false
                  {:standard, :describe} -> false   # v2 is an incremental version over v1
                  {:describe, :standard} -> true    # v1 is an incremental version over v2
                  {:describe, :describe} ->         # need to parse out the bits
                    v1_commits_since > v2_commits_since
                end
              :lt -> false
            end;
          {{_, v1}, {_, v2}} ->
            v1 >  v2
        end
      end)
    |> Enum.map(fn {v, _} -> v end)
  end

  defp parse_version(ver) do
    case Version.parse(ver) do
      {:ok, semver} -> {:semantic, semver}
      :error        -> {:unsemantic, ver}
    end
  end

  @doc """
  Gets a list of {app, vsn} tuples for the current release.

  An optional second parameter enables/disables debug logging of discovered apps.
  """
  @spec get_apps(Mix.Releases.Release.t) :: [{atom, String.t}] | {:error, String.t}
  # Gets all applications which are part of the release application tree
  def get_apps(%Release{name: name, applications: apps}) do
    children = get_apps(App.new(name), [])
    IO.inspect children
    apps = Enum.reduce(apps, children, fn
      _, {:error, _} = err ->
        err
      {a, start_type}, acc ->
        case Enum.any?(acc, fn %App{name: ^a} -> true; _ -> false end) do
          true  -> acc
          false -> get_apps(App.new(a, start_type), acc)
        end
      a, acc when is_atom(a) ->
        case Enum.any?(acc, fn %App{name: ^a} -> true; _ -> false end) do
          true  -> acc
          false -> get_apps(App.new(a), acc)
        end
    end)
    if is_list(apps) do
      Logger.debug "Discovered applications:"
      Enum.each(apps, fn %App{} = app ->
        where = Path.relative_to_cwd(app.path)
        Logger.debug "  #{IO.ANSI.reset}#{app.name}-#{app.vsn}#{IO.ANSI.cyan}\n" <>
          "    from: #{where}", :plain
        case app.applications do
          [] ->
            Logger.debug "    applications: none", :plain
          _  ->
            Logger.debug "    applications:\n" <>
              "      #{Enum.map(app.applications, &Atom.to_string/1) |> Enum.join("\n      ")}", :plain
        end
        case app.included_applications do
          [] ->
            Logger.debug "    includes: none\n", :plain
          _ ->
            Logger.debug "    includes:\n" <>
              "      #{Enum.map(app.included_applications, &Atom.to_string/1) |> Enum.join("\n     ")}", :plain
        end
      end)
    end
    apps
  end
  defp get_apps(nil, acc), do: Enum.uniq(acc)
  defp get_apps({:error, _} = err, _acc), do: err
  defp get_apps(%App{} = app, acc) do
    new_acc = app.applications
    |> Enum.reduce([app|acc], fn
      {:error, _} = err, _acc ->
        err
      a, acc ->
        case Enum.any?(acc, fn %App{name: ^a} -> true; _ -> false end) do
          true -> acc
          false ->
            case App.new(a) do
              nil ->
                acc
              %App{} = app ->
                case get_apps(app, acc) do
                  {:error, _} = err -> err
                  children -> Enum.concat(acc, children)
                end
              {:error, _} = err ->
                err
            end
        end
    end)
    case new_acc do
      {:error, _} = err -> err
      apps -> Enum.uniq(apps)
    end
  end
end

defmodule Mix.Releases.Utils do
  @moduledoc false
  alias Mix.Releases.{Logger, Release, App}

  @doc """
  Loads a template from :distillery's `priv/templates` directory based on the provided name.
  Any parameters provided are configured as bindings for the template

  ## Example

      iex> {:ok, contents} = #{__MODULE__}.template("vm.args", [rel_name: :test])
      ...> String.contains?(contents, "-name test@127.0.0.1")
      true
  """
  @spec template(atom | String.t, Keyword.t) :: {:ok, String.t} | {:error, String.t}
  def template(name, params \\ []) do
    try do
      template_path = Path.join(["#{:code.priv_dir(:distillery)}", "templates", "#{name}.eex"])
      {:ok, EEx.eval_file(template_path, params)}
    rescue
      e ->
        {:error, e.__struct__.message(e)}
    end
  end

  @doc """
  Writes an Elixir/Erlang term to the provided path
  """
  @spec write_term(String.t, term) :: :ok | {:error, term}
  def write_term(path, term) do
    :file.write_file('#{path}', :io_lib.fwrite('~p.\n', [term]), [encoding: :utf8])
  end

  @doc """
  Writes a collection of Elixir/Erlang terms to the provided path
  """
  @spec write_terms(String.t, [term]) :: :ok | {:error, term}
  def write_terms(path, terms) when is_list(terms) do
    contents = String.duplicate("~p.\n\n", Enum.count(terms))
       |> String.to_char_list
       |> :io_lib.fwrite(Enum.reverse(terms))
    :file.write_file('#{path}', contents, [encoding: :utf8])
  end

  @doc """
  Reads a file as Erlang terms
  """
  @spec read_terms(String.t) :: {:ok, [term]} :: {:error, String.t}
  def read_terms(path) do
    case :file.consult(String.to_charlist(path)) do
      {:ok, _} = result ->
        result
      {:error, {line, type, msg}} ->
        {:error, "Parse failed - #{path}@#{line} (#{type}): #{msg}"}
      {:error, reason} ->
        {:error, "Unable to access #{path} (#{reason})"}
    end
  end

  @doc """
  Determines the current ERTS version
  """
  @spec erts_version() :: String.t
  def erts_version, do: "#{:erlang.system_info(:version)}"

  @doc """
  Creates a temporary directory with a random name in a canonical
  temporary files directory of the current system
  (i.e. `/tmp` on *NIX or `./tmp` on Windows)

  Returns an ok tuple with the path of the temp directory, or an error
  tuple with the reason it failed.
  """
  @spec insecure_mkdir_temp() :: {:ok, String.t} | {:error, term}
  def insecure_mkdir_temp() do
    unique_num = trunc(:random.uniform() * 1000000000000)
    tmpdir_path = case :erlang.system_info(:system_architecture) do
                    'win32' ->
                      Path.join(["./tmp", ".tmp_dir#{unique_num}"])
                    _ ->
                      Path.join(["/tmp", ".tmp_dir#{unique_num}"])
                  end
    case File.mkdir_p(tmpdir_path) do
      :ok -> {:ok, tmpdir_path}
      {:error, _} = err -> err
    end
  end

  @doc """
  Given a path to a release output directory, return a list
  of release versions that are present.

  ## Example

      iex> app_dir = Path.join([File.cwd!, "test", "fixtures", "mock_app"])
      ...> output_dir = Path.join([app_dir, "rel", "mock_app"])
      ...> #{__MODULE__}.get_release_versions(output_dir)
      ["0.2.2", "0.2.1-1-d3adb3f", "0.2.1", "0.2.0", "0.1.0"]
  """
  @spec get_release_versions(String.t) :: list(String.t)
  def get_release_versions(output_dir) do
    releases_path = Path.join([output_dir, "releases"])
    case File.exists?(releases_path) do
      false -> []
      true  ->
        releases_path
        |> File.ls!
        |> Enum.reject(fn entry -> entry in ["RELEASES", "start_erl.data"] end)
        |> sort_versions
    end
  end

  @git_describe_pattern ~r/(?<ver>\d+\.\d+\.\d+)-(?<commits>\d+)-(?<sha>[A-Ga-g0-9]+)/
  @doc """
  Sort a list of version strings, in reverse order (i.e. latest version comes first)
  Tries to use semver version compare, but can fall back to regular string compare.
  It also parses git-describe generated version strings and handles ordering them
  correctly.

  ## Example

      iex> #{__MODULE__}.sort_versions(["1.0.2", "1.0.1", "1.0.9", "1.0.10"])
      ["1.0.10", "1.0.9", "1.0.2", "1.0.1"]

      iex> #{__MODULE__}.sort_versions(["0.0.1", "0.0.2", "0.0.1-2-a1d2g3f", "0.0.1-1-deadbeef"])
      ["0.0.2", "0.0.1-2-a1d2g3f", "0.0.1-1-deadbeef", "0.0.1"]
  """
  @spec sort_versions(list(String.t)) :: list(String.t)
  def sort_versions(versions) do
    versions
    |> Enum.map(fn ver ->
        # Special handling for git-describe versions
        compared = case Regex.named_captures(@git_describe_pattern, ver) do
          nil ->
            {:standard, ver, nil}
          %{"ver" => version, "commits" => n, "sha" => sha} ->
            {:describe, <<version::binary, ?+, n::binary, ?-, sha::binary>>, String.to_integer(n)}
        end
        {ver, compared}
      end)
    |> Enum.sort(
      fn {_, {v1type, v1str, v1_commits_since}}, {_, {v2type, v2str, v2_commits_since}} ->
        case { parse_version(v1str), parse_version(v2str) } do
          {{:semantic, v1}, {:semantic, v2}} ->
            case Version.compare(v1, v2) do
              :gt -> true
              :eq ->
                case {v1type, v2type} do
                  {:standard, :standard} -> v1 > v2 # probably always false
                  {:standard, :describe} -> false   # v2 is an incremental version over v1
                  {:describe, :standard} -> true    # v1 is an incremental version over v2
                  {:describe, :describe} ->         # need to parse out the bits
                    v1_commits_since > v2_commits_since
                end
              :lt -> false
            end;
          {{_, v1}, {_, v2}} ->
            v1 >  v2
        end
      end)
    |> Enum.map(fn {v, _} -> v end)
  end

  defp parse_version(ver) do
    case Version.parse(ver) do
      {:ok, semver} -> {:semantic, semver}
      :error        -> {:unsemantic, ver}
    end
  end

  @doc """
  Gets a list of {app, vsn} tuples for the current release.

  An optional second parameter enables/disables debug logging of discovered apps.
  """
  @spec get_apps(Mix.Releases.Release.t) :: [{atom, String.t}] | {:error, String.t}
  # Gets all applications which are part of the release application tree
  def get_apps(%Release{name: name, applications: apps}) do
    children = get_apps(App.new(name), [])
    IO.inspect children
    apps = Enum.reduce(apps, children, fn
      _, {:error, _} = err ->
        err
      {a, start_type}, acc ->
        case Enum.any?(acc, fn %App{name: ^a} -> true; _ -> false end) do
          true  -> acc
          false -> get_apps(App.new(a, start_type), acc)
        end
      a, acc when is_atom(a) ->
        case Enum.any?(acc, fn %App{name: ^a} -> true; _ -> false end) do
          true  -> acc
          false -> get_apps(App.new(a), acc)
        end
    end)
    if is_list(apps) do
      Logger.debug "Discovered applications:"
      Enum.each(apps, fn %App{} = app ->
        where = Path.relative_to_cwd(app.path)
        Logger.debug "  #{IO.ANSI.reset}#{app.name}-#{app.vsn}#{IO.ANSI.cyan}\n" <>
          "    from: #{where}", :plain
        case app.applications do
          [] ->
            Logger.debug "    applications: none", :plain
          _  ->
            Logger.debug "    applications:\n" <>
              "      #{Enum.map(app.applications, &Atom.to_string/1) |> Enum.join("\n      ")}", :plain
        end
        case app.included_applications do
          [] ->
            Logger.debug "    includes: none\n", :plain
          _ ->
            Logger.debug "    includes:\n" <>
              "      #{Enum.map(app.included_applications, &Atom.to_string/1) |> Enum.join("\n     ")}", :plain
        end
      end)
    end
    apps
  end
  defp get_apps(nil, acc), do: Enum.uniq(acc)
  defp get_apps({:error, _} = err, _acc), do: err
  defp get_apps(%App{} = app, acc) do
    new_acc = app.applications
    |> Enum.reduce([app|acc], fn
      {:error, _} = err, _acc ->
        err
      a, acc ->
        case Enum.any?(acc, fn %App{name: ^a} -> true; _ -> false end) do
          true -> acc
          false ->
            case App.new(a) do
              nil ->
                acc
              %App{} = app ->
                case get_apps(app, acc) do
                  {:error, _} = err -> err
                  children -> Enum.concat(acc, children)
                end
              {:error, _} = err ->
                err
            end
        end
    end)
    case new_acc do
      {:error, _} = err -> err
      apps -> Enum.uniq(apps)
    end
  end
end