@@ -6,13 +6,29 @@ defmodule Mix.Tasks.Compile.ElixirTest do
66 import ExUnit.CaptureIO
77 alias Mix.Task.Compiler.Diagnostic
88
9+ @ old_time { { 2010 , 1 , 1 } , { 0 , 0 , 0 } }
10+ @ elixir_otp_version { System . version ( ) , :erlang . system_info ( :otp_release ) }
11+
912 def trace ( event , env ) do
1013 send ( __MODULE__ , { event , env } )
1114 :ok
1215 end
1316
14- @ old_time { { 2010 , 1 , 1 } , { 0 , 0 , 0 } }
15- @ elixir_otp_version { System . version ( ) , :erlang . system_info ( :otp_release ) }
17+ defp mtime ( path ) do
18+ File . stat! ( path ) . mtime
19+ end
20+
21+ defp mark_as_old! ( path ) do
22+ mtime = mtime ( path )
23+ File . touch! ( path , @ old_time )
24+ mtime
25+ end
26+
27+ defp purge_protocol ( module ) do
28+ :code . del_path ( :filename . absname ( ~c" _build/dev/lib/sample/consolidated" ) )
29+ :code . purge ( module )
30+ :code . delete ( module )
31+ end
1632
1733 test "compiles a project without per environment build" do
1834 Mix.ProjectStack . post_config ( build_per_environment: false )
@@ -128,7 +144,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
128144 assert Mix.Tasks.Compile.Elixir . run ( [ "--verbose" ] ) == { :ok , [ ] }
129145 assert_received { :mix_shell , :info , [ "Compiled lib/a.ex" ] }
130146 refute_received { :mix_shell , :info , [ "Compiled lib/b.ex" ] }
131- assert File . stat! ( "_build/dev/lib/sample/.mix/compile.elixir" ) . mtime > @ old_time
147+ assert mtime ( "_build/dev/lib/sample/.mix/compile.elixir" ) > @ old_time
132148
133149 ensure_touched ( __ENV__ . file , "_build/dev/lib/sample/.mix/compile.elixir" )
134150 assert Mix.Tasks.Compile.Elixir . run ( [ "--verbose" ] ) == { :ok , [ ] }
@@ -245,7 +261,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
245261 assert Mix.Tasks.Compile.Elixir . run ( [ "--verbose" ] ) == { :ok , [ ] }
246262 assert_received { :mix_shell , :info , [ "Compiled lib/a.ex" ] }
247263 refute_received { :mix_shell , :info , [ "Compiled lib/b.ex" ] }
248- assert File . stat! ( "_build/dev/lib/sample/.mix/compile.elixir" ) . mtime > @ old_time
264+ assert mtime ( "_build/dev/lib/sample/.mix/compile.elixir" ) > @ old_time
249265 end )
250266 after
251267 Application . put_env ( :elixir , :dbg_callback , { Macro , :dbg , [ ] } )
@@ -482,7 +498,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
482498 assert recompile . ( ) == { :ok , [ ] }
483499 assert_received { :mix_shell , :info , [ "Compiled lib/a.ex" ] }
484500 refute_received { :mix_shell , :info , [ "Compiled lib/b.ex" ] }
485- assert File . stat! ( "_build/dev/lib/sample/.mix/compile.elixir" ) . mtime > @ old_time
501+ assert mtime ( "_build/dev/lib/sample/.mix/compile.elixir" ) > @ old_time
486502
487503 # Changing lock recompiles
488504 File . write! ( "mix.lock" , """
@@ -494,7 +510,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
494510 assert recompile . ( ) == { :ok , [ ] }
495511 assert_received { :mix_shell , :info , [ "Compiled lib/a.ex" ] }
496512 refute_received { :mix_shell , :info , [ "Compiled lib/b.ex" ] }
497- assert File . stat! ( "_build/dev/lib/sample/.mix/compile.elixir" ) . mtime > @ old_time
513+ assert mtime ( "_build/dev/lib/sample/.mix/compile.elixir" ) > @ old_time
498514
499515 # Removing a lock recompiles
500516 File . write! ( "mix.lock" , """
@@ -506,7 +522,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
506522 assert recompile . ( ) == { :ok , [ ] }
507523 assert_received { :mix_shell , :info , [ "Compiled lib/a.ex" ] }
508524 refute_received { :mix_shell , :info , [ "Compiled lib/b.ex" ] }
509- assert File . stat! ( "_build/dev/lib/sample/.mix/compile.elixir" ) . mtime > @ old_time
525+ assert mtime ( "_build/dev/lib/sample/.mix/compile.elixir" ) > @ old_time
510526
511527 # Adding an unknown dependency returns :ok but does not recompile
512528 File . write! ( "mix.lock" , """
@@ -584,9 +600,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
584600 Mix.Tasks.Compile . run ( [ ] )
585601 assert Mix.Dep.ElixirSCM . read ( ) == { :ok , @ elixir_otp_version , Mix.SCM.Path }
586602
587- assert File . stat! ( "_build/dev/lib/sample/.mix/compile.elixir_scm" ) . mtime >
588- @ old_time
589-
603+ assert mtime ( "_build/dev/lib/sample/.mix/compile.elixir_scm" ) > @ old_time
590604 refute File . exists? ( "_build/dev/lib/sample/consolidated/.to_be_removed" )
591605 end )
592606 end
@@ -608,8 +622,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
608622 Mix.Tasks.Compile . run ( [ ] )
609623 assert Mix.Dep.ElixirSCM . read ( ) == { :ok , @ elixir_otp_version , Mix.SCM.Path }
610624
611- assert File . stat! ( "_build/dev/lib/sample/.mix/compile.elixir_scm" ) . mtime >
612- @ old_time
625+ assert mtime ( "_build/dev/lib/sample/.mix/compile.elixir_scm" ) > @ old_time
613626 end )
614627 end
615628
@@ -720,38 +733,6 @@ defmodule Mix.Tasks.Compile.ElixirTest do
720733 end )
721734 end
722735
723- test "purges consolidation path if asked" do
724- in_fixture ( "no_mixfile" , fn ->
725- File . write! ( "lib/a.ex" , """
726- defmodule A do
727- defstruct []
728- end
729-
730- defimpl Inspect, for: A do
731- def inspect(_, _), do: "sample"
732- end
733- """ )
734-
735- Mix.Project . push ( MixTest.Case.Sample )
736- assert Mix.Tasks.Compile . run ( [ ] ) == { :ok , [ ] }
737- assert inspect ( struct ( A , [ ] ) ) == "sample"
738-
739- purge ( [ A , B , Inspect.A ] )
740- Mix.Task . clear ( )
741-
742- assert capture_io ( :stderr , fn ->
743- { :ok , [ _ ] } = Mix.Tasks.Compile . run ( [ "--force" ] )
744- end ) =~
745- "the Inspect protocol has already been consolidated"
746-
747- purge ( [ A , B , Inspect.A ] )
748- Mix.Task . clear ( )
749- consolidation = Mix.Project . consolidation_path ( )
750- args = [ "--force" , "--purge-consolidation-path-if-stale" , consolidation ]
751- assert Mix.Tasks.Compile . run ( args ) == { :ok , [ ] }
752- end )
753- end
754-
755736 test "recompiles mtime changed files if content changed but not length" do
756737 in_fixture ( "no_mixfile" , fn ->
757738 Mix.Project . push ( MixTest.Case.Sample )
@@ -1344,7 +1325,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
13441325 assert_received { :mix_shell , :info , [ "Compiled lib/a.ex" ] }
13451326
13461327 assert Mix.Tasks.Compile.Elixir . run ( [ "--verbose" ] ) == { :noop , [ ] }
1347- assert File . stat! ( "lib/a.ex" ) . mtime == @ old_time
1328+ assert mtime ( "lib/a.ex" ) == @ old_time
13481329
13491330 Agent . update ( :mix_recompile_raise , fn _ -> true end )
13501331
@@ -1729,4 +1710,180 @@ defmodule Mix.Tasks.Compile.ElixirTest do
17291710 assert Mix.Tasks.Compile.Elixir . run ( [ "--no-optional-deps" ] ) == { :ok , [ ] }
17301711 end )
17311712 end
1713+
1714+ describe "consolidation protocols" do
1715+ test "with local protocols" , context do
1716+ in_tmp ( context . test , fn ->
1717+ Mix.Project . push ( MixTest.Case.Sample )
1718+
1719+ File . mkdir_p! ( "lib" )
1720+ assert Mix.Task . run ( "compile" )
1721+
1722+ # Define a local protocol
1723+ File . write! ( "lib/protocol.ex" , """
1724+ defprotocol Compile.Protocol do
1725+ def foo(a, b)
1726+ end
1727+ """ )
1728+
1729+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :ok , [ ] }
1730+ mark_as_old! ( "_build/dev/lib/sample/consolidated/Elixir.Compile.Protocol.beam" )
1731+
1732+ # Implement a local protocol
1733+ File . write! ( "lib/impl.ex" , """
1734+ defimpl Compile.Protocol, for: Integer do
1735+ def foo(a, b), do: a + b
1736+ end
1737+ """ )
1738+
1739+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :ok , [ ] }
1740+
1741+ assert mark_as_old! ( "_build/dev/lib/sample/consolidated/Elixir.Compile.Protocol.beam" ) !=
1742+ @ old_time
1743+
1744+ # Delete a local implementation
1745+ File . rm! ( "lib/impl.ex" )
1746+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :ok , [ ] }
1747+
1748+ assert mark_as_old! ( "_build/dev/lib/sample/consolidated/Elixir.Compile.Protocol.beam" ) !=
1749+ @ old_time
1750+
1751+ # Delete a local protocol
1752+ File . rm! ( "lib/protocol.ex" )
1753+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :ok , [ ] }
1754+ refute File . regular? ( "_build/dev/lib/sample/consolidated/Elixir.Compile.Protocol.beam" )
1755+ end )
1756+ end
1757+
1758+ test "compiles after converting a protocol into a standard module" , context do
1759+ in_tmp ( context . test , fn ->
1760+ Mix.Project . push ( MixTest.Case.Sample )
1761+
1762+ File . mkdir_p! ( "lib" )
1763+ Mix.Task . run ( "compile" )
1764+ purge_protocol ( Compile.Protocol )
1765+
1766+ # Define a local protocol
1767+ File . write! ( "lib/protocol.ex" , """
1768+ defprotocol Compile.Protocol do
1769+ def foo(a)
1770+ end
1771+
1772+ defimpl Compile.Protocol, for: Integer do
1773+ def foo(a), do: a
1774+ end
1775+ """ )
1776+
1777+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :ok , [ ] }
1778+ mark_as_old! ( "_build/dev/lib/sample/consolidated/Elixir.Compile.Protocol.beam" )
1779+ File . rm! ( "lib/protocol.ex" )
1780+
1781+ # Define a standard module
1782+ File . write! ( "lib/protocol.ex" , """
1783+ defmodule Compile.Protocol do
1784+ end
1785+ """ )
1786+
1787+ purge_protocol ( Compile.Protocol )
1788+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :ok , [ ] }
1789+
1790+ # Delete a local protocol
1791+ File . rm! ( "lib/protocol.ex" )
1792+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :ok , [ ] }
1793+ refute File . regular? ( "_build/dev/lib/sample/consolidated/Elixir.Compile.Protocol.beam" )
1794+ end )
1795+ end
1796+
1797+ test "with deps protocols" , context do
1798+ in_tmp ( context . test , fn ->
1799+ Mix.Project . push ( MixTest.Case.Sample )
1800+
1801+ File . mkdir_p! ( "lib" )
1802+ Mix.Task . run ( "compile" )
1803+ purge_protocol ( String.Chars )
1804+ mark_as_old! ( "_build/dev/lib/sample/consolidated/Elixir.String.Chars.beam" )
1805+
1806+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :noop , [ ] }
1807+ assert mtime ( "_build/dev/lib/sample/consolidated/Elixir.String.Chars.beam" ) == @ old_time
1808+
1809+ # Implement a deps protocol
1810+ File . write! ( "lib/struct.ex" , """
1811+ defmodule Compile.Protocol.Struct do
1812+ defstruct a: nil
1813+ defimpl String.Chars do
1814+ def to_string(_), do: "ok"
1815+ end
1816+ end
1817+ """ )
1818+
1819+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :ok , [ ] }
1820+
1821+ assert mark_as_old! ( "_build/dev/lib/sample/consolidated/Elixir.String.Chars.beam" ) !=
1822+ @ old_time
1823+
1824+ # Delete the local implementation
1825+ File . rm! ( "lib/struct.ex" )
1826+ assert Mix.Tasks.Compile.Elixir . run ( [ ] ) == { :ok , [ ] }
1827+
1828+ assert mark_as_old! ( "_build/dev/lib/sample/consolidated/Elixir.String.Chars.beam" ) !=
1829+ @ old_time
1830+ end )
1831+ end
1832+
1833+ test "keep relative path to their source" do
1834+ in_fixture ( "no_mixfile" , fn ->
1835+ Mix.Project . push ( MixTest.Case.Sample )
1836+ Mix.Task . run ( "compile" )
1837+
1838+ # Load consolidated
1839+ :code . add_patha ( ~c" _build/dev/lib/sample/consolidated" )
1840+ :code . purge ( Enumerable )
1841+ :code . delete ( Enumerable )
1842+
1843+ try do
1844+ Enumerable . impl_for! ( :oops )
1845+ rescue
1846+ Protocol.UndefinedError ->
1847+ assert [ { _ , _ , _ , [ file: ~c" lib/enum.ex" ] ++ _ } | _ ] = __STACKTRACE__
1848+ else
1849+ _ ->
1850+ flunk ( "Enumerable.impl_for!/1 should have failed" )
1851+ after
1852+ purge_protocol ( Enumerable )
1853+ end
1854+ end )
1855+ end
1856+
1857+ test "purges consolidation path if asked" do
1858+ in_fixture ( "no_mixfile" , fn ->
1859+ File . write! ( "lib/a.ex" , """
1860+ defmodule A do
1861+ defstruct []
1862+ end
1863+
1864+ defimpl Inspect, for: A do
1865+ def inspect(_, _), do: "sample"
1866+ end
1867+ """ )
1868+
1869+ Mix.Project . push ( MixTest.Case.Sample )
1870+ assert Mix.Tasks.Compile . run ( [ ] ) == { :ok , [ ] }
1871+ assert inspect ( struct ( A , [ ] ) ) == "sample"
1872+
1873+ purge ( [ A , B , Inspect.A ] )
1874+ Mix.Task . clear ( )
1875+
1876+ assert capture_io ( :stderr , fn ->
1877+ { :ok , [ _ ] } = Mix.Tasks.Compile . run ( [ "--force" ] )
1878+ end ) =~
1879+ "the Inspect protocol has already been consolidated"
1880+
1881+ purge ( [ A , B , Inspect.A ] )
1882+ Mix.Task . clear ( )
1883+ consolidation = Mix.Project . consolidation_path ( )
1884+ args = [ "--force" , "--purge-consolidation-path-if-stale" , consolidation ]
1885+ assert Mix.Tasks.Compile . run ( args ) == { :ok , [ ] }
1886+ end )
1887+ end
1888+ end
17321889end
0 commit comments