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

mocked modules are not returned to normal state when verify/1 returns #8

Open
cypherfox opened this issue Nov 30, 2017 · 13 comments
Open

Comments

@cypherfox
Copy link
Contributor

Sometime after the switch to gen_statem (it may be completelely unconnected to that change) I regularly get errors like the one below.

When inserting a timer:sleep(100) after the em:verify/1 in the previous test of my eunit suite all works fine. I have started to add a sleep after every verify to work around this.

I have had a look at the code, but cannot find a race condition (at least no according to the documentation of the Erlang API functions that are called by erlymock)

=ERROR REPORT==== 29-Nov-2017::18:23:46 ===
** State machine <0.4811.0> terminating
** Last event = {{call,{<0.4809.0>,#Ref<0.4180725794.4003201027.186836>}},
                 {replay,infinity}}
** When server state  = {programming,
                         {state,<0.4809.0>,infinity,
                          [{expectation,#Ref<0.4180725794.4003201027.186833>,
                            {root,#Ref<0.4180725794.4003201027.186829>},
                            quperl_client_event_mgr,new_object,
                            [<<1,2,3,4>>],
                            {return,ok},
                            []},
                           {expectation,#Ref<0.4180725794.4003201027.186831>,
                            {root,#Ref<0.4180725794.4003201027.186829>},
                            quperl_client_cache,create_object,
                            [<<1,2,3,4>>,[{name,"foo"}]],
                            {return,ok},
                            []}],
                          [],
                          [{expectation,#Ref<0.4180725794.4003201027.186835>,
                            {root,#Ref<0.4180725794.4003201027.186829>},
                            quperl_client_event_mgr,fail,
                            [#Fun<quperl_client_util.is_object_id.1>,
                             txn_mgr_terminated],
                            {return,ok},
                            []}],
                          [],[],[],undefined,no_error}}
** Reason for termination = error:{bad_return_from_state_function,
                                   {em_error_module_already_mocked,
                                    quperl_client_event_mgr}}
** Callback mode = state_functions
** Stacktrace =
**  [{gen_statem,parse_event_result,8,[{file,"gen_statem.erl"},{line,1318}]},
     {gen_statem,loop_event,6,[{file,"gen_statem.erl"},{line,1015}]},
     {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]
@sheyll
Copy link
Owner

sheyll commented Dec 19, 2017

Thanks for reporting. I will try to reproduce this, then fix it. Do you have a failing minimal self contained test case?

@cypherfox
Copy link
Contributor Author

Unfortunately not. This seems to be a classical heisenbug. I have test modules with >20 test cases run fine on my local, fairly fast machine, as well as on our CI pipeline server, then suddenly either may act up. Sometimes a retry of the whole test suite works, sometimes not. I have just started to add these sleeps without further reflection, as I need the tests to run.
It is time wasted, better the computers time than mine ;-)

@sheyll
Copy link
Owner

sheyll commented Dec 19, 2017

Thanks for reporting, we use em for production code, too, and I know about heisenbugs in unit tests

@sheyll
Copy link
Owner

sheyll commented Feb 15, 2018

I still haven't figured it out, I will meet a guy tomorrow, who might be able to help...

@cypherfox
Copy link
Contributor Author

Thank you for staying on it.

@sheyll
Copy link
Owner

sheyll commented Feb 16, 2018

Ok, I can reproduce it now.
If it helps temporarily, things seem to work with 6.0.0-dev.

sheyll pushed a commit that referenced this issue Feb 19, 2018
Introduce the `em_mocking_queue' to serialize module mocking to prevent module
loading races.

The race condition is between checking if a module is already mocked and
loading a mock module, such that it might happen that two processes both
overwrite a module with a mock version.
@sheyll
Copy link
Owner

sheyll commented Feb 19, 2018

I hope this finally fixes it... but since it is a Heisenbug, we'll have to see.

@cypherfox
Copy link
Contributor Author

Yes, it seems to do the trick. Many thanks for the great work.

sheyll pushed a commit that referenced this issue Feb 20, 2018
* Replace `em_mocking_queue` by `em_module_loader`
* Discourage concurrent mocking
* Fix the race condition introduced in 7.1.0
* Add `em:lock()' and `em:unlock()' to mark sections of code
  that would appreciate that no modules are mocked.
* Hopefully fix #8
@sheyll sheyll reopened this Feb 20, 2018
@sheyll
Copy link
Owner

sheyll commented Feb 20, 2018

Oh well... still breaks our tests :/

Another version is on its way.

@sheyll
Copy link
Owner

sheyll commented Feb 20, 2018

Version 7.2.2 is out, and it should fix this.

@sheyll sheyll closed this as completed Feb 20, 2018
@cypherfox
Copy link
Contributor Author

Hmm..no. I go lots of those after turning to 7.2.2. The test was running with 7.1.0:

=ERROR REPORT==== 20-Feb-2018::04:56:39 ===
** State machine <0.8172.0> terminating
** Last event = {{call,{<0.8158.0>,#Ref<0.3074200492.3073638401.158745>}},
                 {replay,infinity}}
** When server state  = {programming,
                            {state,<0.8158.0>,infinity,[],[],[],[],
                                [lb_server_locator],
                                undefined,no_error}}
** Reason for termination = error:{bad_return_from_state_function,
                                   {'failed to load modules',
                                    {error,
                                     'erly-mock moduler loading server internal error, please report this error'}}}
** Callback mode = state_functions
** Stacktrace =
**  [{gen_statem,parse_event_result,8,[{file,"gen_statem.erl"},{line,1318}]},
     {gen_statem,loop_event,6,[{file,"gen_statem.erl"},{line,1015}]},
     {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]

@cypherfox
Copy link
Contributor Author

Things worked after a fashion with 7.1.0.

The only case where the mocked module was not released seem to be when;

M = em:new(),
em:strict(M, module_a, func1, []),
Step = em:strict(M, module_b, func2, []),
em:replay(M)

Pid = module_c:start_link(),

em:await(M, Step),
em:verify(M),

module_c:stop(Pid),

where the module_c:stop/1 would call a function in module_b. If I wait a few hundred miliseconds before module_c:stop/1, it worked fine with 7.1.0.

Yes, I could mock the last function to be called in module_b as well, but we have lot of bad tests, that will take some time to do. :(

@sheyll sheyll reopened this Feb 21, 2018
@sheyll
Copy link
Owner

sheyll commented Feb 21, 2018

Ok ... I will try to make a test-case out of your example and then try to fix it, if it breaks...

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

2 participants