Hardware.millis mocking

Hi Forum,
My code uses hardware.millis() to keep track of the elapsed time.
I want to write an unit test of a modules that uses hardware.millis(). I am specially interested in testing my module behavior when hardware.millis() overflows and becomes negative without having to wait approx. 25 days.
Questions:

  • Is it possible to OVERRIDE hardware.millis() method ?
  • Perhaps is necessary to mock the whole hardware global table in order to mock just one method like hardware.millis() ? Are there other alternatives?

As far as I remember, you would need to mock the entire hardware table - well, the calls you use - in order to intercept that call. I remember this being done by a customer previously for the imp. calls but can’t find the code in github at this point.

Hugo,
Is it possible to override just the call to
hardware.millis()
?

No, I don’t think so. You need to make a new global table called hardware, and then implement all the hardware.XXX calls within that. I’m not the squirrel expert when it gets this deep, but @phil might be able to give an example of how you would call the original handlers from an override table.

Here’s one way to override millis() and still pass through all the other methods and member objects. It would be trickier if any of the methods had explicit parameters, and also obviously changes typeof(hardware), but might be enough for your use case.

class MockableHardware {
    _hardware = null;
    constructor(hardware) { _hardware = hardware; }
    function millis() { return 123456; }
    function _get(key) {
        local v = _hardware[key];
        return (typeof(v) == "function") ? function() { return v.call(_hardware); } : v;
    }
};
hardware <- MockableHardware(hardware);

Some test code for the above:

server.log(hardware.getdeviceid());
server.log(hardware.lightlevel());
server.log(hardware.micros());
server.log(hardware.millis());
try {
    server.log(hardware.vbat());
} catch(e) {
    server.log("vbat not supported by "+imp.info().type);
}
server.log(hardware.voltage());
server.log(hardware.wakereason());
server.log(hardware.pin1); // imp001
server.log("configure" in hardware.pin1); // imp001

Thank you @hugo , @phil
I will try this way.

@phil
I have tried :

class MockHardware {
    
    _hardware = null;

    constructor(hardware) {
        _hardware = hardware;
    }

    function millis() {
        return 123123; 
    }

    function _get(key) {
        local v = _hardware[key];
        return (typeof(v) == "function") ? function() { return v.call(_hardware); } : v;
    }

};

The way I use it :

hardware <- MockHardware(hardware);
try {
    server.log("=> hardware.voltage()= " + hardware.voltage());
} catch(e) {
    server.log("=> Error: " + e);
}

Throws :
[Device]|=> Error: the index 'voltage' does not exist

Any hints ?

That code works (i.e. returns a float) for me with an imp001. What imp type are you using?

Do any of the other hardware methods throw an error?

@phil,

I use imp005.
I modified the code a bit for debugging purposes and I see type “meta”

class MockHardware {

    _hardware = null;

    constructor(hardware) {
        _hardware = hardware;
    }

    function millis() {
        return 123123; 
    }

    function _get(key) {
        local v = _hardware[key];
        server.log("key=" + key +" typeof " + v + " = " + typeof(v));
        return (typeof(v) == "function") ? function() { return v.call(_hardware); } : v;
    }

};

At the beginning of my code I use

hardware <- MockHardware(hardware);
try {
    server.log("=> hardware.getdeviceid()= " + hardware.getdeviceid());
    server.log("configure" in hardware.pinK);
} catch(e) {
    server.log("=> Error: " + e);
}

I get

|2023-01-05T16:19:33.684 +00:00|[Device]|=> hardware.getdeviceid()= 5000d8c46a56f57a|
| --- | --- | --- |
|2023-01-05T16:19:33.687 +00:00|[Device]|key=pinK typeof (meta : 0x542da8) = meta|
|2023-01-05T16:19:33.687 +00:00|[Device]|true|
|2023-01-05T16:19:33.689 +00:00|[Device]|key=pinK typeof (meta : 0x542da8) = meta|
|2023-01-05T16:19:33.689 +00:00|[Device]|key=pinH typeof (meta : 0x53f040) = meta|

Attached is the complete error log

error_log.zip (775 Bytes)

I think that’s working then. getdeviceid() is returning a sensible value and meta is the expected type for imp API objects like hardware itself and the GPIO pins. The configure method is visible on the passed-through pinK so those objects should all work correctly.

It’s just voltage() that will fail, because that method isn’t implemented for imp005.

…not implemented as the imp005 does not have any ADC hardware, vs being something missing from the API!