Lookup errors in eEPGCache::lookupEventTime()

Issue #591 resolved
prl created an issue

This bug report assumes that the correct behaviour of the undocumented eEPGCache::lookupEventTime() parameter direction on lookup of the EPG data associated with service is:
direction > 0 return the earliest event that starts after t.
direction == 0 return the event that spans t. If t is spanned by a gap in the EPG, return None.
direction < 0 return the event immediately before the event that spans t. If t is spanned by a gap in the EPG, return the event immediately before the gap.

Time-based lookup fails to meet that assumed specification in the following cases:

  • If for some event event event.begin < t && t < event.end and direction < 0, event is returned, instead of the expected event preceding it.
  • If for the last event in a service's EPG, lastEvent.begin < t && t < lastEvent.end and direction == 0, -1 (None in a Python call) is returned, not lastEvent.
  • If for the last event in a service's EPG, lastEvent.begin < t && t < lastEvent.end and direction < 0, -1 (None in a Python call) is returned, not the event preceding lastEvent.
  • If t is after the end of the EPG data, lastEvent.end < t and direction == -1, -1 (None in a Python call) is returned, not lastEvent.

Part of the problem is the use of It->second.byTime.lower_bound(t) when direction <= 0. using lower_bound(t) means that depending on the value of t either the event spanning t is returned, or the event following it. This complicates the required code, and not all cases appear to be handled correctly.

The code also depends on the behaviour of x-- when x == It->second.byTime.begin() changing the iterator to x == It->second.byTime.end(). This is the case in the C++ STL used in the 16.1 Beyonwiz build environment (and also in Ubuntu 16.04), but not in OS X XCode 7.3). The usage also seems counter-intuitive.

I think that eEPGCache::lookupEventTime() should be re-written to use It->second.byTime.upper_bound(t) for all values of direction, and the reliance on the behaviour of x-- when x != It->second.byTime.begin() should be avoided by testing for x != It->second.byTime.begin() before decrementing x.

I have code in test that re-implements eEPGCache::lookupEventTime() in that way, and also documents the behaviour of direction.

Replication steps

The problems are not simple to replicate from within the EPG. Call the following Python function from anywhere convenient in the Python code to demonstrate all the above bugs. The parameter serviceref must be of type eServiceReference.

def testLookupEventTime(serviceref):
    from time import time, ctime
    epgCache = eEPGCache.getInstance()

    print "testLookupEventTime"
    print "testLookupEventTime tests now+5h"
    # Find a representative event in the body of the EPG, ~5 hours in the future.
    evt = epgCache.lookupEventTime(serviceref, int(time() + 5 * 3600), 0)
    if evt:
        et = evt.getBeginTime()

    # Compare a "direction = -1" lookup when t == event.start
    # with a lookup where event.start < t < event.end.
    # A lookup with t == event.start and "direction = 0"
    # is shown for comparison
        evt = epgCache.lookupEventTime(serviceref, et, -1)
        print "testLookupEventTime evt now+5h, -1", evt and evt.getEventName(), evt and ctime(evt.getBeginTime())
        evt = epgCache.lookupEventTime(serviceref, et + 1, -1)
        print "testLookupEventTime evt now+5h+1, -1", evt and evt.getEventName(), evt and ctime(evt.getBeginTime())
        evt = epgCache.lookupEventTime(serviceref, et, 0)
        print "testLookupEventTime evt now+5h, 0", evt and evt.getEventName(), evt and ctime(evt.getBeginTime())
    else:
        print "testLookupEventTime tests now+5h - base event not found"

    # Find the last event in the service's EPG
    epgCache.startTimeQuery(serviceref)
    evt = None
    evtc =  epgCache.getNextTimeEntry()
    while evtc:
        evt = evtc
        evtc =  epgCache.getNextTimeEntry()
    print "testLookupEventTime"
    print "testLookupEventTime tests on last event"
    if evt:
        et = evt.getBeginTime()

    # Compare a "direction = -1" lookup when t == lastEvent.start
    # with a lookup where lastEvent.begin < t < lastEvent.end
        evt = epgCache.lookupEventTime(serviceref, et, -1)
        print "testLookupEventTime evt last et, -1", evt and evt.getEventName(), evt and ctime(evt.getBeginTime())
        evt = epgCache.lookupEventTime(serviceref, et + 1, -1)
        print "testLookupEventTime evt last et+1, -1", evt and evt.getEventName(), evt and ctime(evt.getBeginTime())

    # Compare a "direction = 0" lookup when t == lastEvent.start
    # with a lookup where lastEvent.begin < t < lastEvent.end
        evt = epgCache.lookupEventTime(serviceref, et, 0)
        print "testLookupEventTime evt last et, 0", evt and evt.getEventName(), evt and ctime(evt.getBeginTime())
        evt = epgCache.lookupEventTime(serviceref, et + 1, 0)
        print "testLookupEventTime evt last et+1, 0", evt and evt.getEventName(), evt and ctime(evt.getBeginTime())
    else:
        print "testLookupEventTime tests on last event - last event not found"

    print "testLookupEventTime"
    print "testLookupEventTime tests beyond last event"

    # lookup for event preceding Fri Jan 15 08:00:00 2027 (should be last eventin the EPG)
    evt = epgCache.lookupEventTime(serviceref, 1800000000, -1)
    print "testLookupEventTime evt 1800000000, -1", evt and evt.getEventName(), evt and ctime(evt.getBeginTime())

Comments (2)

  1. Log in to comment