Randomly getting CompilationError on tests

In a project I am working, I am randomly getting compilation errors while running tests. The stack trace is like this (changed file names):

== Compilation error in file test/some_test.exs ==
** (UndefinedFunctionError) function File.regular?/1 is undefined or private
    (elixir) File.regular?("~/dev/some_project//test/some_test.exs")
    (elixir) lib/code.ex:1144: Code.find_file/2
    (elixir) lib/code.ex:754: Code.require_file/2
    (elixir) lib/kernel/parallel_compiler.ex:209: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

It complains about File.regular?/1 that is part of the core library. In my test_helper.exs I have:

ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter])

So I thought that it could be it (the first line there). I’ve added {:module, File} = Code.ensure_compiled(File) but it did not help.

Not sure where to look next. I am on:

  • Erlang 21.1 (asdf)
  • Elixir 1.7.3-otp-21 (asdf)
  • Linux arm64 (Fedora 28)

Has anyone suffered a similar issue? I’d appreciate some help as this is killing productivity. I think it might have to do with the language server but can’t be 100% sure.

Edited it is AMD64 not ARM64… my bad =/

1 Like

None of those functions call into File.regular?/1. The only one I could find in ExUnit was ExUnit.FailuresManifest.write!/2 which is called from ExUnit.RunnerStats in a handle_cast implementation that takes a tuple starting with :suite_finished … but that shouldn’t crap out during running tests, if anything it would do so during building Elixir itself …

I see that you are building on arm64 … could it be that this is a deeper issue like running out of memory when running specific test suites on a low-mem (relatively?) system? If not that, perhaps something arm64 specific. Are you using a self-built OTP / Elixir stack?

Are any of your tests using File.cd! or similar? If so, this means the test is running and changing the directory while the test suite is still loading files. You have to set these tests to async: false (or remove the async config, which is false by default).

1 Like

I have more than 16GB of fre RAM and plenty of disk space so I guess that is not it…

Also it is AMD64 not arm… sorry for my mistake. Should have written x64 that is easier.

I will double check that. My guess is that the language server trie to compile the files too but what you say makes sense.

JUnitFormatter creates a report when the suite ends but that’s about it. I create the dir on test_helper.exs and JUnitFormatter writes the file when it ends.

I really think we are not using File.cd! in our test suite. Be right back when I am 100% positive on that.

EDITED: 100% sure we are not using a single call to File. in our test base (just ran grep -rnw '.' -e 'File.' in test folder)

Compilation would definitely trigger this, especially in an umbrella project. But isn’t that supposed to be happening in a separate process? If you can by any chance isolate this, I would be glad to take a look.

I will switch the language server off and try to simulate the error. At least that will exclude that possibility. I think its that because on CI it doesn’t fail. :slight_smile:

(should have mentioned it before)

Be right back with results …

EDITED nope. It’s not the language server… Can reproduce it even without the language server and without JUnitFormatter configured.

It shows on different test files each time and I’ve marked them with async: false but it didn’t help. It looks like it happens before running.

Found it… god damnit!

One endpoint of our API replies with the content of a static file. Like a constant response that we cache at application startup…

In our tests someone used the Mock library to mock the File.read/1 call. That test was async…

The code is like this:

    with_mock File, read: fn _filename -> {:ok, "12345"} end do
      assert FileLoader.load("priv://dir/file") == "12345"

My earlier grep couldn’t find it. So I started running tests with mix test --trace which showed me the bad test… Should have done that before posting here! Sorry.

I hate mocks… marking it as async: false seems to have solved it. Thanks for your help and support!


In place of mocking you can always pass module names into functions just as general usage, that’s what I do and it let’s you have a lot more control as well. :slight_smile:

This is like the Witness pattern for note. :slight_smile:


Totally agree. Will refactor it (eventually) to avoid this. This is classical technical debt being carried over which is not the language’s fault. We, the dev team, all learned something from it though :slight_smile:

1 Like