Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to stub Integer.to_string/2 #28

Open
bterone opened this issue Aug 6, 2021 · 6 comments
Open

Unable to stub Integer.to_string/2 #28

bterone opened this issue Aug 6, 2021 · 6 comments

Comments

@bterone
Copy link

bterone commented Aug 6, 2021

Issue

Trying to stub the Integer.to_string/2 method does not work.

Environment

  • Elixir 1.11.4 (compiled with Erlang/OTP 23)
  • Erlang/OTP 24 [erts-12.0.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Steps to reproduce

# test_helper.exs
Mimic.copy(Integer)

ExUnit.start()
# test/sometest.exs
defmodule SomeTest do
  use ExUnit.Case
  use Mimic

  test "greets the world" do
    stub(Integer, :to_string, fn _ -> "STUB" end)
    stub(Integer, :to_string, fn _, _ -> "STUB" end)

    random = Integer.to_string(5)

    assert random == "STUB"
  end
end

It seems the above test will fail, but if it's for some other method, like Integer.digits/1 it passes.

Not really sure what could be the issue without further investigation. 🤔

EDIT: I'll be more than happy to open an example repo reproducing the issue 😄

@jimsynz
Copy link
Contributor

jimsynz commented Aug 8, 2021

@bterone thanks for the detailed bug report. Out of interest, can you try stubbing :erlang.integer_to_binary/2 instead? It seems that Integer.to_string/1..2 is inlined into that function by the compiler.

@bterone
Copy link
Author

bterone commented Aug 10, 2021

Hi, thanks for taking your time to respond 😄 , I'm a bit unsure how to copy the :erlang atom to stub. Do I need some sort of adapter module to stub, or is there some macro that could help me out? 🤔

@jimsynz
Copy link
Contributor

jimsynz commented Aug 10, 2021

Hey @bterone. All module names in Elixir and Erlang are atoms. You should just be able to do Mimic.copy(:erlang) and then stub the function as per usual.

@edgurgel
Copy link
Owner

I think in general we want to avoid touching the standard lib.

I guess we could add a validation to copy to not allow some modules? 🤔

We can probably get all Elixir modules using this:

iex(3)> :application.get_key(:elixir, :modules)
{:ok,
 [Access, Agent.Server, Agent, Application, ArgumentError, ArithmeticError,
  Atom, BadArityError, BadBooleanError, BadFunctionError, BadMapError,
  BadStructError, Base, Behaviour, Bitwise, Calendar.ISO,
  Calendar.TimeZoneDatabase, Calendar.UTCOnlyTimeZoneDatabase, Calendar,
  CaseClauseError, Code.Formatter, Code.Identifier, Code.LoadError,
  Code.Typespec, Code, Collectable.BitString, Collectable.File.Stream,
  Collectable.HashDict, Collectable.HashSet, Collectable.IO.Stream,
  Collectable.List, Collectable.Map, Collectable.MapSet, Collectable,
  CompileError, CondClauseError, Config.Provider, Config.Reader, Config,
  Date.Range, Date, DateTime, Dict, DynamicSupervisor, Enum.EmptyError,
  Enum.OutOfBoundsError, Enum, Enumerable.Date.Range, ...]}

Not sure about Erlang though

@jimsynz
Copy link
Contributor

jimsynz commented Aug 11, 2021

Oh I agree. I just thought it was worth seeing if this fixed it because it may hint at something weird about the compiler inlining.

@bterone
Copy link
Author

bterone commented Aug 12, 2021

Hey @bterone. All module names in Elixir and Erlang are atoms. You should just be able to do Mimic.copy(:erlang) and then stub the function as per usual.

Hi @jimsynz, I've tried using Mimic.copy(:erlang) but it seems to fail when trying to fetch the compile options from that atom as it doesn't exist 🤔

** (Protocol.UndefinedError) protocol Enumerable not implemented for nil of type Atom. This protocol is implemented for the following type(s): HashSet, Range, Map, Function, List, Stream, Date.Range, HashDict, GenEvent.Stream, MapSet, File.Stream, IO.Stream
    (elixir 1.11.4) lib/enum.ex:1: Enumerable.impl_for!/1
    (elixir 1.11.4) lib/enum.ex:141: Enumerable.reduce/3
    (elixir 1.11.4) lib/enum.ex:3473: Enum.filter/2
    (mimic 1.5.0) lib/mimic/module.ex:65: Mimic.Module.compiler_options/1
    (mimic 1.5.0) lib/mimic/module.ex:43: Mimic.Module.rename_module/2
    (mimic 1.5.0) lib/mimic/module.ex:27: Mimic.Module.replace!/1
    (mimic 1.5.0) lib/mimic.ex:344: Mimic.copy/1
    test/test_helper.exs:4: (file)

When checking that line that was causing the issue
Screen Shot 2021-08-12 at 6 04 20 PM

Screen Shot 2021-08-12 at 5 59 46 PM

Even modifying the Keyword.get(:options) to give an empty [] as default has an error when we try to do :compile.forms(forms, compiler_options(module)) on line 43 🤔

** (CaseClauseError) no case clause matching: {:error, [{'erlang.erl', [{{353, 2}, :erl_lint, {:bad_module, {:erlang, :adler32, 1}}}, {{359, 2}, :erl_lint, {:bad_module, {:erlang, :adler32, 2}}}, {{366, 2}, :erl_lint, {:bad_module, {:erlang, :adler32_combine, 3}}}, {{374, 2}, :erl_lint, {:bad_module, {:erlang, :append_element, 2}}}, {{518, 2}, :erl_lint, {:bad_module, {:erlang, :bump_reductions, 1}}}, {{531, 2}, :erl_lint, {:bad_module, {:erlang, :call_on_load_function, 1}}}, {{537, 2}, :erl_lint, {:bad_module, {:erlang, :cancel_timer, 1}}}, {{546, 2}, :erl_lint, {:bad_module, {:erlang, :cancel_timer, 2}}}, {{602, 2}, :erl_lint, {:bad_module, {:erlang, :crc32, 1}}}, {{608, 2}, :erl_lint, {:bad_module, {:erlang, :crc32, 2}}}, {{615, 2}, :erl_lint, {:bad_module, {:erlang, :crc32_combine, 3}}}, {{629, 2}, :erl_lint, {:bad_module, {:erlang, :decode_packet, 3}}}, {{726, 2}, :erl_lint, {:bad_module, {:erlang, :delete_element, 2}}}, {{773, 2}, :erl_lint, {:bad_module, {:erlang, :display, 1}}}, {{779, 2}, :erl_lint, {:bad_module, {:erlang, :display_nl, 0}}}, {{784, 2}, :erl_lint, {:bad_module, {:erlang, :display_string, 1}}}, {{790, 2}, :erl_lint, {:bad_module, {:erlang, :dt_append_vm_tag_data, 1}}}, {{797, 2}, :erl_lint, {:bad_module, {:erlang, :dt_get_tag, 0}}}, {{802, 2}, :erl_lint, {:bad_module, {:erlang, :dt_get_tag_data, 0}}}, {{807, 2}, :erl_lint, {:bad_module, {:erlang, :dt_prepend_vm_tag_data, 1}}}, {{814, 2}, :erl_lint, {:bad_module, {:erlang, :dt_put_tag, 1}}}, {{820, 2}, :erl_lint, {:bad_module, {:erlang, :dt_restore_tag, 1}}}, {{826, 2}, :erl_lint, {:bad_module, {:erlang, :dt_spread_tag, 1}}}, {{888, 2}, :erl_lint, {:bad_module, {:erlang, :exit_signal, 2}}}, {{895, 2}, :erl_lint, {:bad_module, {:erlang, :external_size, 1}}}, {{901, 2}, :erl_lint, {:bad_module, {:erlang, :external_size, 2}}}, {{908, 2}, :erl_lint, {:bad_module, {:erlang, :finish_loading, 1}}}, {{917, 2}, :erl_lint, {:bad_module, {:erlang, :finish_after_on_load, 2}}}, {{970, 2}, :erl_lint, {:bad_module, {:erlang, :fun_info, 2}}}, {{978, 2}, :erl_lint, {:bad_module, {:erlang, :fun_info_mfa, 1}}}, {{987, 2}, :erl_lint, {:bad_module, {:erlang, :fun_to_list, 1}}}, {{993, 2}, :erl_lint, {:bad_module, {:erlang, :function_exported, 3}}}, {{1067, 2}, :erl_lint, {:bad_module, {:erlang, :garbage_collect_message_area, 0}}}, {{1099, 2}, :erl_lint, {:bad_module, {:erlang, :get_module_info, 1}}}, {{1157, 2}, :erl_lint, {:bad_module, {:erlang, :has_prepared_code_on_load, 1}}}, {{1163, 2}, :erl_lint, {:bad_module, {:erlang, :hibernate, 3}}}, {{1171, 2}, :erl_lint, {:bad_module, {:erlang, :insert_element, 3}}}, {{1204, 2}, :erl_lint, {:bad_module, {:erlang, :iolist_to_iovec, ...}}}, {{1215, 2}, :erl_lint, {:bad_module, {:erlang, ...}}}, {{1316, 2}, :erl_lint, {:bad_module, {...}}}, {{1322, 2}, :erl_lint, {:bad_module, ...}}, {{1347, 2}, :erl_lint, {...}}, {{1356, ...}, :erl_lint, ...}, {{...}, ...}, {...}, ...]}], [{'erlang.erl', [{{247, 2}, :erl_lint, {:unused_type, {:fun_info_item, 0}}}, {{259, 2}, :erl_lint, {:unused_type, {:seq_trace_info_returns, 0}}}, {{265, 2}, :erl_lint, {:unused_type, {:system_profile_option, 0}}}, {{274, 2}, :erl_lint, {:unused_type, {:system_monitor_option, 0}}}, {{282, 2}, :erl_lint, {:unused_type, {:raise_stacktrace, 0}}}, {{291, 2}, :erl_lint, {:unused_type, {:trace_flag, 0}}}, {{343, 2}, :erl_lint, {:unused_type, {:trace_info_return, 0}}}, {{2119, 2}, :erl_lint, {:unused_type, {:module_info_key, 0}}}, {{2595, 2}, :erl_lint, {:unused_type, {:scheduler_bind_type, 0}}}, {{2699, 2}, :erl_lint, {:unused_type, {:trace_pattern_mfa, 0}}}, {{2717, 2}, :erl_lint, {:unused_type, {:trace_pattern_flag, 0}}}, {{2751, 2}, :erl_lint, {:unused_type, {:cpu_topology, 0}}}, {{3451, 2}, :erl_lint, {:unused_type, {:dst, 0}}}, {{3976, 2}, :erl_lint, {:unused_type, {:memory_type, 0}}}]}]}
    (mimic 1.5.0) lib/mimic/module.ex:49: Mimic.Module.rename_module/2
    (mimic 1.5.0) lib/mimic/module.ex:27: Mimic.Module.replace!/1
    (mimic 1.5.0) lib/mimic.ex:344: Mimic.copy/1
    test/test_helper.exs:5: (file)

Not really sure if it's even possible to copy :erlang. 💭

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants