Add windows version of ci (#1045)

* Add windows version of ci

Config autocrlf to input on Windows
start epmd in background

* Update .github/workflows/test.yaml

* Update .github/workflows/test.yaml

* Update .github/workflows/test.yaml

* Fix tests

* Fix ownership of cached files

* Fix tests

* Increase timeouts

* Run tests on Windows only on main

Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
Howard Su 2022-04-12 02:34:31 +08:00 committed by GitHub
parent a2802248ab
commit 31b0a9f7d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 166 additions and 79 deletions

View file

@ -32,7 +32,7 @@ jobs:
- name: Check warnings
run: mix compile --warnings-as-errors
- name: Run tests
run: elixir --cookie "COOKIEFORTESTS" -S mix test
run: mix test
- name: Install Node
uses: actions/setup-node@v2
with:
@ -50,3 +50,41 @@ jobs:
run: npm run format-check --prefix assets
- name: Run assets tests
run: npm test --prefix assets
windows:
runs-on: windows-latest
if: github.ref == 'refs/heads/main'
env:
MIX_ENV: test
steps:
- name: Configure Git
run: git config --global core.autocrlf input
- uses: actions/checkout@v2
- name: Install Erlang & Elixir
uses: erlef/setup-beam@v1
with:
otp-version: '24.0'
elixir-version: '1.13.0'
- name: Start epmd
run: cmd /c "START /b epmd"
working-directory: ${{ env.INSTALL_DIR_FOR_OTP }}/erts-12.0.4/bin
# Add tar that supports symlinks, see https://github.com/actions/virtual-environments/issues/4679
- name: Add tar.exe
run: |
"C:\Program Files\Git\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8
- name: Cache Mix
uses: actions/cache@v2
with:
path: |
deps
_build
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-
- name: Install mix dependencies
run: mix deps.get
- name: Check formatting
run: mix format --check-formatted
- name: Check warnings
run: mix compile --warnings-as-errors
- name: Run tests
run: mix test

View file

@ -168,7 +168,7 @@ defimpl Livebook.FileSystem, for: Livebook.FileSystem.Local do
containing_dir = Path.dirname(destination_path)
with :ok <- File.mkdir_p(containing_dir),
:ok <- File.rename(source_path, destination_path) do
:ok <- rename_or_move(source_path, destination_path) do
:ok
else
{:error, error} ->
@ -178,6 +178,18 @@ defimpl Livebook.FileSystem, for: Livebook.FileSystem.Local do
end
end
defp rename_or_move(source_path, destination_path) do
with {:error, :exdev} <- File.rename(source_path, destination_path) do
# For files on different file systems, try to copy and remove instead
with {:ok, _paths} <- File.cp_r(source_path, destination_path),
{:ok, _paths} <- File.rm_rf(source_path) do
:ok
else
{:error, error, _paths} -> {:error, error}
end
end
end
def etag_for(file_system, path) do
with :ok <- ensure_local(file_system) do
case File.stat(path) do

View file

@ -86,8 +86,8 @@ defmodule Livebook.FileSystem.Utils do
and handles sequences such as "." and "..".
"""
@spec resolve_unix_like_path(FileSystem.path(), String.t()) :: FileSystem.t()
def resolve_unix_like_path(dir_path, subject) do
assert_dir_path!(dir_path)
def resolve_unix_like_path(relative_to, subject) do
dir_path = relative_to |> Path.dirname() |> ensure_dir_path()
subject =
if Path.basename(subject) in [".", ".."] do

View file

@ -249,7 +249,7 @@ defmodule Livebook.Utils do
url
|> URI.parse()
|> Map.update!(:path, fn path ->
path |> Path.dirname() |> Path.join(relative_path) |> Path.expand()
Livebook.FileSystem.Utils.resolve_unix_like_path(path, relative_path)
end)
|> URI.to_string()
end
@ -444,20 +444,20 @@ defmodule Livebook.Utils do
end
@doc """
Returns a URL (including localhost) to open the given `url` as a notebook
Returns a URL (including localhost) to open the given `path` as a notebook
iex> Livebook.Utils.notebook_open_url("https://example.com/foo.livemd")
"http://localhost:4002/open?path=https%3A%2F%2Fexample.com%2Ffoo.livemd"
iex> Livebook.Utils.notebook_open_url("/data/foo.livemd")
"http://localhost:4002/open?path=%2Fdata%2Ffoo.livemd"
iex> Livebook.Utils.notebook_open_url("https://my_host", "https://example.com/foo.livemd")
"https://my_host/open?path=https%3A%2F%2Fexample.com%2Ffoo.livemd"
iex> Livebook.Utils.notebook_open_url("https://my_host", "/data/foo.livemd")
"https://my_host/open?path=%2Fdata%2Ffoo.livemd"
"""
def notebook_open_url(base_url \\ LivebookWeb.Endpoint.access_struct_url(), url) do
def notebook_open_url(base_url \\ LivebookWeb.Endpoint.access_struct_url(), path) do
base_url
|> URI.parse()
|> Map.replace!(:path, "/open")
|> append_query("path=#{URI.encode_www_form(url)}")
|> append_query("path=#{URI.encode_www_form(path)}")
|> URI.to_string()
end

View file

@ -18,60 +18,61 @@ defmodule Livebook.FileSystem.FileTest do
file_system = FileSystem.Local.new()
assert_raise ArgumentError,
~s{expected an expanded absolute path, got: "/dir/nested/../file.txt"},
~s{expected an expanded absolute path, got: "#{p("/dir/nested/../file.txt")}"},
fn ->
FileSystem.File.new(file_system, "/dir/nested/../file.txt")
FileSystem.File.new(file_system, p("/dir/nested/../file.txt"))
end
end
test "uses default file system path if non is given" do
file_system = FileSystem.Local.new(default_path: "/dir/")
assert %FileSystem.File{path: "/dir/"} = FileSystem.File.new(file_system)
default_path = p("/dir/")
file_system = FileSystem.Local.new(default_path: default_path)
assert %FileSystem.File{path: ^default_path} = FileSystem.File.new(file_system)
end
end
describe "local/1" do
test "uses the globally configured local file system instance" do
assert FileSystem.File.local("/path").file_system == Livebook.Config.local_filesystem()
assert FileSystem.File.local(p("/path")).file_system == Livebook.Config.local_filesystem()
end
end
describe "relative/2" do
test "ignores the file path if an absolute path is given" do
file_system = FileSystem.Local.new()
file = FileSystem.File.new(file_system, "/dir/nested/file.txt")
file = FileSystem.File.new(file_system, p("/dir/nested/file.txt"))
assert %FileSystem.File{file_system: ^file_system, path: "/other/file.txt"} =
FileSystem.File.resolve(file, "/other/file.txt")
assert %FileSystem.File{file_system: ^file_system, path: p("/other/file.txt")} =
FileSystem.File.resolve(file, p("/other/file.txt"))
end
test "resolves a relative path against a regular file" do
file_system = FileSystem.Local.new()
file = FileSystem.File.new(file_system, "/dir/nested/file.txt")
file = FileSystem.File.new(file_system, p("/dir/nested/file.txt"))
assert %FileSystem.File{file_system: ^file_system, path: "/dir/other/other_file.txt"} =
assert %FileSystem.File{file_system: ^file_system, path: p("/dir/other/other_file.txt")} =
FileSystem.File.resolve(file, "../other/other_file.txt")
end
test "resolves a relative path against a directory file" do
file_system = FileSystem.Local.new()
dir = FileSystem.File.new(file_system, "/dir/nested/")
dir = FileSystem.File.new(file_system, p("/dir/nested/"))
assert %FileSystem.File{file_system: ^file_system, path: "/dir/nested/file.txt"} =
assert %FileSystem.File{file_system: ^file_system, path: p("/dir/nested/file.txt")} =
FileSystem.File.resolve(dir, "file.txt")
end
test "resolves a relative directory path" do
file_system = FileSystem.Local.new()
file = FileSystem.File.new(file_system, "/dir/nested/file.txt")
file = FileSystem.File.new(file_system, p("/dir/nested/file.txt"))
assert %FileSystem.File{file_system: ^file_system, path: "/dir/other/"} =
assert %FileSystem.File{file_system: ^file_system, path: p("/dir/other/")} =
FileSystem.File.resolve(file, "../other/")
assert %FileSystem.File{file_system: ^file_system, path: "/dir/nested/"} =
assert %FileSystem.File{file_system: ^file_system, path: p("/dir/nested/")} =
FileSystem.File.resolve(file, ".")
assert %FileSystem.File{file_system: ^file_system, path: "/dir/"} =
assert %FileSystem.File{file_system: ^file_system, path: p("/dir/")} =
FileSystem.File.resolve(file, "..")
end
end
@ -80,10 +81,10 @@ defmodule Livebook.FileSystem.FileTest do
test "returns true if file path has a trailing slash" do
file_system = FileSystem.Local.new()
dir = FileSystem.File.new(file_system, "/dir/")
dir = FileSystem.File.new(file_system, p("/dir/"))
assert FileSystem.File.dir?(dir)
file = FileSystem.File.new(file_system, "/dir/file.txt")
file = FileSystem.File.new(file_system, p("/dir/file.txt"))
refute FileSystem.File.dir?(file)
end
end
@ -92,10 +93,10 @@ defmodule Livebook.FileSystem.FileTest do
test "returns true if file path has no trailing slash" do
file_system = FileSystem.Local.new()
dir = FileSystem.File.new(file_system, "/dir/")
dir = FileSystem.File.new(file_system, p("/dir/"))
refute FileSystem.File.regular?(dir)
file = FileSystem.File.new(file_system, "/dir/file.txt")
file = FileSystem.File.new(file_system, p("/dir/file.txt"))
assert FileSystem.File.regular?(file)
end
end
@ -104,10 +105,10 @@ defmodule Livebook.FileSystem.FileTest do
test "returns path basename" do
file_system = FileSystem.Local.new()
dir = FileSystem.File.new(file_system, "/dir/")
dir = FileSystem.File.new(file_system, p("/dir/"))
assert FileSystem.File.name(dir) == "dir"
file = FileSystem.File.new(file_system, "/dir/file.txt")
file = FileSystem.File.new(file_system, p("/dir/file.txt"))
assert FileSystem.File.name(file) == "file.txt"
end
end
@ -116,21 +117,23 @@ defmodule Livebook.FileSystem.FileTest do
test "given a directory, returns the parent directory" do
file_system = FileSystem.Local.new()
dir = FileSystem.File.new(file_system, "/parent/dir/")
assert FileSystem.File.new(file_system, "/parent/") == FileSystem.File.containing_dir(dir)
dir = FileSystem.File.new(file_system, p("/parent/dir/"))
assert FileSystem.File.new(file_system, p("/parent/")) ==
FileSystem.File.containing_dir(dir)
end
test "given a file, returns the containing directory" do
file_system = FileSystem.Local.new()
file = FileSystem.File.new(file_system, "/dir/file.txt")
assert FileSystem.File.new(file_system, "/dir/") == FileSystem.File.containing_dir(file)
file = FileSystem.File.new(file_system, p("/dir/file.txt"))
assert FileSystem.File.new(file_system, p("/dir/")) == FileSystem.File.containing_dir(file)
end
test "given the root directory, returns itself" do
file_system = FileSystem.Local.new()
file = FileSystem.File.new(file_system, "/")
file = FileSystem.File.new(file_system, p("/"))
assert file == FileSystem.File.containing_dir(file)
end
end

View file

@ -8,9 +8,11 @@ defmodule Livebook.FileSystem.LocalTest do
describe "new/1" do
test "raises when :default_path is not a directory" do
assert_raise ArgumentError, ~s{expected a directory path, got: "/notebook.livemd"}, fn ->
Local.new(default_path: "/notebook.livemd")
end
assert_raise ArgumentError,
~s{expected a directory path, got: "#{p("/notebook.livemd")}"},
fn ->
Local.new(default_path: p("/notebook.livemd"))
end
end
end
@ -21,8 +23,8 @@ defmodule Livebook.FileSystem.LocalTest do
end
test "returns custom directory path if configured" do
file_system = Local.new(default_path: "/dir/")
assert FileSystem.default_path(file_system) == "/dir/"
file_system = Local.new(default_path: p("/dir/"))
assert FileSystem.default_path(file_system) == p("/dir/")
end
end
@ -489,25 +491,29 @@ defmodule Livebook.FileSystem.LocalTest do
test "resolves relative paths" do
file_system = Local.new()
assert "/dir/" = FileSystem.resolve_path(file_system, "/dir/", "")
assert "/dir/file.txt" = FileSystem.resolve_path(file_system, "/dir/", "file.txt")
assert "/dir/nested/" = FileSystem.resolve_path(file_system, "/dir/", "nested/")
assert "/dir/" = FileSystem.resolve_path(file_system, "/dir/", ".")
assert "/" = FileSystem.resolve_path(file_system, "/dir/", "..")
assert p("/dir/") = FileSystem.resolve_path(file_system, p("/dir/"), "")
assert p("/dir/file.txt") = FileSystem.resolve_path(file_system, p("/dir/"), "file.txt")
assert p("/dir/nested/") = FileSystem.resolve_path(file_system, p("/dir/"), "nested/")
assert p("/dir/") = FileSystem.resolve_path(file_system, p("/dir/"), ".")
assert p("/") = FileSystem.resolve_path(file_system, p("/dir/"), "..")
assert "/file.txt" =
FileSystem.resolve_path(file_system, "/dir/", "nested/../.././file.txt")
assert p("/file.txt") =
FileSystem.resolve_path(file_system, p("/dir/"), "nested/../.././file.txt")
end
test "resolves absolute paths" do
file_system = Local.new()
assert "/" = FileSystem.resolve_path(file_system, "/dir/", "/")
assert "/file.txt" = FileSystem.resolve_path(file_system, "/dir/", "/file.txt")
assert "/nested/" = FileSystem.resolve_path(file_system, "/dir/", "/nested/")
assert p("/") = FileSystem.resolve_path(file_system, p("/dir/"), p("/"))
assert p("/file.txt") = FileSystem.resolve_path(file_system, p("/dir/"), p("/file.txt"))
assert p("/nested/") = FileSystem.resolve_path(file_system, p("/dir/"), p("/nested/"))
assert "/nested/file.txt" =
FileSystem.resolve_path(file_system, "/dir/", "///nested///other/..///file.txt")
assert p("/nested/file.txt") =
FileSystem.resolve_path(
file_system,
p("/dir/"),
p("///nested///other/..///file.txt")
)
end
end
end

View file

@ -3451,7 +3451,7 @@ defmodule Livebook.Session.DataTest do
test "updates data with the given path" do
data = Data.new()
file = Livebook.FileSystem.File.local("/path/to/file.livemd")
file = Livebook.FileSystem.File.local(p("/path/to/file.livemd"))
operation = {:set_file, self(), file}
assert {:ok, %{file: ^file}, []} = Data.apply_operation(data, operation)

View file

@ -1,30 +1,32 @@
defmodule Livebook.Session.FileGuardTest do
use ExUnit.Case, async: false
import Livebook.TestHelpers
alias Livebook.Session.FileGuard
alias Livebook.FileSystem
test "lock/2 returns an error if the given file is already locked" do
file = FileSystem.File.local("/some/path")
file = FileSystem.File.local(p("/some/path"))
assert :ok = FileGuard.lock(file, self())
assert {:error, :already_in_use} = FileGuard.lock(file, self())
end
test "lock/2 is agnostic to irrelevant file system configuration" do
fs1 = FileSystem.Local.new(default_path: "/path/1/")
fs2 = FileSystem.Local.new(default_path: "/path/2/")
fs1 = FileSystem.Local.new(default_path: p("/path/1/"))
fs2 = FileSystem.Local.new(default_path: p("/path/2/"))
# The file system has different configuration, but it's the same resource
file1 = FileSystem.File.new(fs1, "/some/path")
file2 = FileSystem.File.new(fs2, "/some/path")
file1 = FileSystem.File.new(fs1, p("/some/path"))
file2 = FileSystem.File.new(fs2, p("/some/path"))
assert :ok = FileGuard.lock(file1, self())
assert {:error, :already_in_use} = FileGuard.lock(file2, self())
end
test "unlock/1 unlocks the given file" do
file = FileSystem.File.local("/some/path")
file = FileSystem.File.local(p("/some/path"))
assert :ok = FileGuard.lock(file, self())
:ok = FileGuard.unlock(file)
@ -32,7 +34,7 @@ defmodule Livebook.Session.FileGuardTest do
end
test "file is automatically unloacked when the owner process termiantes" do
file = FileSystem.File.local("/some/path")
file = FileSystem.File.local(p("/some/path"))
owner = spawn(fn -> :ok end)
:ok = FileGuard.lock(file, owner)

View file

@ -346,7 +346,7 @@ defmodule Livebook.SessionTest do
Session.set_file(session.pid, file)
# Wait for the session to deal with the files
Process.sleep(100)
Process.sleep(500)
assert {:ok, true} =
FileSystem.File.exists?(FileSystem.File.resolve(tmp_dir, "images/test.jpg"))
@ -368,7 +368,7 @@ defmodule Livebook.SessionTest do
Session.set_file(session.pid, nil)
# Wait for the session to deal with the files
Process.sleep(200)
Process.sleep(500)
assert {:ok, true} = FileSystem.File.exists?(image_file)

View file

@ -2,6 +2,7 @@ defmodule LivebookWeb.FileSelectComponentTest do
use LivebookWeb.ConnCase, async: true
import Phoenix.LiveViewTest
import Livebook.TestHelpers
alias Livebook.FileSystem
alias LivebookWeb.FileSelectComponent
@ -18,7 +19,7 @@ defmodule LivebookWeb.FileSelectComponentTest do
end
test "does not show parent directory when in root" do
file = FileSystem.File.local("/")
file = FileSystem.File.local(p("/"))
refute render_component(FileSelectComponent, attrs(file: file)) =~ ".."
end
@ -26,7 +27,7 @@ defmodule LivebookWeb.FileSelectComponentTest do
Keyword.merge(
[
id: 1,
file: FileSystem.File.local("/"),
file: FileSystem.File.local(p("/")),
extnames: [".livemd"],
running_files: []
],

View file

@ -16,10 +16,12 @@ defmodule LivebookWeb.HomeLiveTest do
assert {:error, {:live_redirect, %{to: to}}} =
view
|> element("button", "New notebook")
|> element(~s/[role="navigation"] button/, "New notebook")
|> render_click()
assert to =~ "/sessions/"
close_session_by_path(to)
end
describe "file selection" do
@ -55,6 +57,8 @@ defmodule LivebookWeb.HomeLiveTest do
|> render_click()
assert to =~ "/sessions/"
close_session_by_path(to)
end
@tag :tmp_dir
@ -173,6 +177,7 @@ defmodule LivebookWeb.HomeLiveTest do
{:ok, view, _} = live(conn, to)
assert render(view) =~ "My notebook - fork"
close_session_by_path(to)
Session.close(session.pid)
end
@ -192,8 +197,6 @@ defmodule LivebookWeb.HomeLiveTest do
|> render_click()
refute render(view) =~ session.id
Session.close(session.pid)
end
test "close all selected sessions using bulk action", %{conn: conn} do
@ -237,10 +240,10 @@ defmodule LivebookWeb.HomeLiveTest do
|> render_click()
|> follow_redirect(conn)
assert to =~ "/sessions/"
{:ok, view, _} = live(conn, to)
assert render(view) =~ "Welcome to Livebook"
close_session_by_path(to)
end
describe "notebook import" do
@ -255,10 +258,12 @@ defmodule LivebookWeb.HomeLiveTest do
|> element("form", "Import")
|> render_submit(%{data: %{content: notebook_content}})
{path, _flash} = assert_redirect(view, 1000)
{path, _flash} = assert_redirect(view, 5000)
{:ok, view, _} = live(conn, path)
assert render(view) =~ "My notebook"
close_session_by_path(path)
end
test "should show info flash with information about the imported notebook", %{conn: conn} do
@ -272,10 +277,12 @@ defmodule LivebookWeb.HomeLiveTest do
|> element("form", "Import")
|> render_submit(%{data: %{content: notebook_content}})
{_path, flash} = assert_redirect(view, 1000)
{path, flash} = assert_redirect(view, 5000)
assert flash["info"] =~
"You have imported a notebook, no code has been executed so far. You should read and evaluate code as needed."
close_session_by_path(path)
end
test "should show warning flash when the imported notebook have errors", %{conn: conn} do
@ -292,10 +299,12 @@ defmodule LivebookWeb.HomeLiveTest do
|> element("form", "Import")
|> render_submit(%{data: %{content: notebook_content}})
{_path, flash} = assert_redirect(view, 1000)
{path, flash} = assert_redirect(view, 5000)
assert flash["warning"] =~
"We found problems while importing the file and tried to autofix them:\n- Downgrading all headings, because 3 instances of heading 1 were found"
close_session_by_path(path)
end
end
@ -316,6 +325,8 @@ defmodule LivebookWeb.HomeLiveTest do
{:ok, view, _} = live(conn, to)
assert render(view) =~ "My notebook"
close_session_by_path(to)
end
@tag :tmp_dir
@ -329,6 +340,8 @@ defmodule LivebookWeb.HomeLiveTest do
{:ok, view, _} = live(conn, to)
assert render(view) =~ "My notebook"
close_session_by_path(to)
end
test "redirects to the import form on error", %{conn: conn} do
@ -363,13 +376,15 @@ defmodule LivebookWeb.HomeLiveTest do
test "opens a file when livebook file is passed", %{conn: conn, tmp_dir: tmp_dir} do
notebook_path = Path.join(tmp_dir, "notebook.livemd")
:ok = File.write(notebook_path, "# Notebook OPEN section")
:ok = File.write(notebook_path, "# My notebook")
assert {:error, {:live_redirect, %{flash: %{}, to: to}}} =
live(conn, "/open?path=#{notebook_path}")
{:ok, view, _} = live(conn, to)
assert render(view) =~ "Notebook OPEN section"
assert render(view) =~ "My notebook"
close_session_by_path(to)
end
end
@ -387,4 +402,9 @@ defmodule LivebookWeb.HomeLiveTest do
path
end
defp close_session_by_path("/sessions/" <> session_id) do
{:ok, session} = Sessions.fetch_session(session_id)
Session.close(session.pid)
end
end

View file

@ -39,4 +39,9 @@ defmodule Livebook.TestHelpers do
end
end)
end
@doc """
Converts a Unix-like absolute path into OS-compatible absolute path.
"""
defmacro p("/" <> path), do: Path.expand("/") <> path
end

View file

@ -45,4 +45,4 @@ erl_docs_available? = Code.fetch_docs(:gen_server) != {:error, :chunk_not_found}
exclude = []
exclude = if erl_docs_available?, do: exclude, else: Keyword.put(exclude, :erl_docs, true)
ExUnit.start(assert_receive_timeout: 1_000, exclude: exclude)
ExUnit.start(assert_receive_timeout: 1_500, exclude: exclude)