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

Prevent infinite loops with an ability to disable "instantaneous" (0 clock advancement) builtin hardware functions #241

Open
RobTillaart opened this issue Dec 9, 2020 · 8 comments
Labels
ci scripts The test runner scripts enhancement New feature or request

Comments

@RobTillaart
Copy link

RobTillaart commented Dec 9, 2020

Issue Summary

In a unit test I have a loop that uses micros() to do a loop for a certain time.
As micros() always returns zero the loop will never end as intended.

One should also check millis() ?

@ianfixes
Copy link
Collaborator

ianfixes commented Dec 9, 2020

Part of this is covered by #145 -- the need to have the mocking framework respond in a hardware-like way (time passing, etc). It's a messy problem at any scale, because we'll be working around the fact that Arduino has never really contended with the idea of dependency injection or testability; as a consequence, libraries may contain a lot of loops that assume (e.g.) a clock that increments all by itself. Which is a reasonable assumption!

The other part of this is to be able to set a timeout for an individual unit test (which should have a sensible default like "60 seconds"), so that the test runner itself can't hang. I'm going to repurpose this issue for that timeout.

@ianfixes ianfixes added ci scripts The test runner scripts enhancement New feature or request labels Dec 9, 2020
@ianfixes ianfixes changed the title micros() always return 0 in unit test Prevent infinite loops with an ability to disable "instantaneous" (0 clock advancement) builtin hardware functions Dec 10, 2020
@ianfixes
Copy link
Collaborator

#136 has more examples of the necessary behavior, but as this is a more general request I'm wondering if I could get away with a global "API calls cause this much delay" value. That would eliminate the possibility of infinite loops based on time (although not on something like waiting for an input signal). I'll keep this open to track the simplest option, which would be a long of how much to delay every time any one of the functions is called.

@RobTillaart
Copy link
Author

Idea also send in email to Ian

think the micros might be implemented simple and quite realistic.

At start of a unit test a global var STARTTIME is set to the system time in some units

micros()  { return (system time - STARTTIME) * TIME2US * TIMESPEED; }  
millis()  { return (system time - STARTTIME) * TIME2MS * TIMESPEED ; } 

TIME2US is a constant depending on the accuracy of system time

TIMESPEED Is a positive number that the user can set, (default == 1) to simulate time faster than normal
e.g. TIMESPEED = 9.1, will make the clock tick 9.1x faster.

@ianfixes
Copy link
Collaborator

The problem here is the need to make the mocked hardware perfectly deterministic so that you can properly test that the libraries are doing proper calculation.

For an oversimplified example, if you wanted to measure the slope of an analog input signal you'd want to measure change in voltage divided by change in time. To expect an exact value in the unit test, you need to be able to control the inputs -- including the clock -- with that same exactness.

This is not much of an issue unless you are testing a library that (1) uses while loops in functions and (2) uses hardware state as exit criteria for those loops. Would I be mistaken in thinking that using while loops in that way is limited to cases where the underlying hardware is really what's being tested, and not so much the software?

@RobTillaart
Copy link
Author

RobTillaart commented Dec 10, 2020 via email

@ianfixes
Copy link
Collaborator

unless one can inject either predetermined values

Have you looked at the GODMODE documentation or examples at all? Injecting values to be read from digital or analog pins is one of the first features I added:
https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md#pin-futures

@RobTillaart
Copy link
Author

Yes, seen that
However as micros() stays at 0 filing a future array will not solve the issue. I could patch the library code and count the number of samples (expect 200-220 needed ) and let the loop stop when i have enough samples. But then it becomes non production code and makes it no sense to test.

To be continued

@ianfixes
Copy link
Collaborator

Yes... you're right. What would your ideal pattern be for orchestrating a set of state changes for your library code to "discover" during a unit test? I've struggled with this question a fair amount trying to balance "ergonomics" with expressiveness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ci scripts The test runner scripts enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants