123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- #!/usr/bin/env python
- #
- # This file is Copyright (c) 2010-2019 by the GPSD project
- # BSD terms apply: see the file COPYING in the distribution root for details.
- """
- usage: regress-builder [-r limit] [-s seed]
- Test-compile gpsd with each of every possible combination of device-driver
- options, excluding stub drivers we don't support yet. This does a good
- job of catching bugs after driver API changes.
- The -r option sets Monte Carlo mode (random-walk through configuration space)
- and limits the number of tests. The -s, if given, sets a seed; the default is
- to seed from the system time.
- If no options are given, the script performs a sequential test of all
- possibilities. This requires time on the order of 2 ** n where n is
- the count of options.
- ***IMPORTANT***:
- Not only has this program not been updated for Python 3, but it hasn't been
- touched in such a long time that it still expects to use make rather than
- SCons to do builds (and probably has outdated notions of the drivers as well).
- Although updating it for Python 3 doesn't look too difficult, it doesn't make
- sense to do so without first restoring it to correct operation with Python 2,
- since the former fixes would otherwise be untestable.
- """
- import getopt
- import os
- import random
- import sys
- import time
- driver_names = (
- "aivdm",
- "ashtech",
- # "earthmate",
- "evermore",
- "fv18",
- "garmin",
- "garmintxt",
- "geostar",
- "gpsclock",
- "itrax",
- "mtk3301",
- "navcom",
- "nmea",
- "ntrip",
- "oncore",
- "oceanserver",
- "rtcm104v2",
- # "rtcm104v3",
- "sirf",
- "superstar2",
- # "tnt",
- "tripmate",
- "tsip",
- "ubx",
- "timing",
- "clientdebug",
- "oldstyle",
- "ntpshm",
- "pps",
- "reconfigure",
- "controlsend",
- )
- cyclesize = 2 ** len(driver_names)
- def Sequential():
- "Generate sequential test vectors for exhautive search."
- for i in xrange(cyclesize):
- yield i
- return
- def MonteCarlo(seed):
- "Generate a random shuffle of test vectors for Monte Carlo testing."
- # All the magic here is in the choice of modulus. Any odd number will
- # be relatively prime to any power of two and thus be sufficient to step
- # around the cycle hitting each number exactly once. We'd like adjacent
- # stops to have a large distance from each other. Number theory says the
- # best way to do this is to choose a modulus close to cyclesize / phi,
- # where phi is the golden ratio (1 + 5**0.5)/2.
- modulus = int(cyclesize / 1.618033980)
- if modulus % 2 == 0:
- modulus += 1
- for i in xrange(cyclesize):
- yield (seed + i * modulus) % cyclesize
- return
- class TestFactory:
- "Manage compilation tests."
- Preamble = '''
- env X_LIBS="" \\
- CPPFLAGS="-I/usr/local/include " \\
- LDFLAGS=" -L/usr/local/lib -g" \\
- CFLAGS="-g -O2 -W -Wall" \\
- ./configure --prefix=/home/gpsd --disable-shared \\
- '''
- def n2v(self, n):
- "Number to test-vector"
- v = [0] * len(driver_names)
- i = 0
- while n > 0:
- v[i] = n % 2
- i += 1
- n = n >> 1
- return v
- def test_header(self, vector):
- hdr = ""
- for (i, name) in enumerate(driver_names):
- hdr += "--" + ("disable", "enable")[vector[i]] + "-" + name + " "
- return hdr[:-1]
- def make(self, vector):
- if os.system("make 2>&1 > /dev/null"):
- print("FAILED: ", self.test_header(vector))
- else:
- print("SUCCEEDED:", self.test_header(vector))
- def configure_build(self, vector):
- test = TestFactory.Preamble
- for (i, name) in enumerate(driver_names):
- test += (" --" + ("disable", "enable")[vector[i]] +
- "-" + name + " \\\n")
- test += "\n"
- if os.system("(" + test + ") >/dev/null"):
- print("configure FAILED:", self.test_header(vector))
- else:
- self.make(vector)
- def filter(self, vector):
- "Tell us whether this combination needs to be tested."
- # No drivers at all won't even configure
- if vector == [0] * len(driver_names):
- return False
- compiled_in = map(lambda e: e[1], filter(lambda e: e[0],
- zip(vector, driver_names)))
- def on(name):
- return name in compiled_in
- def off(name):
- return name not in compiled_in
- # START OF FILTERS
- #
- # Some types require NMEA support and shouldn't be built without it.
- if off("nmea") and (on("fv18") or on("earthmate") or on("tripmate")):
- return False
- # Earthmate, FV-18 and TripMate code scopes don't overlap.
- if on("fv18") + on("earthmate") + on("tripmate") not in (0, 3):
- return False
- # SiRF has no overlapping code scopes with the DLE-led protocols
- # (TSIP, Garmin, Evermore) but the DLE-led protocols have
- # overlaps in the packet sniffer.
- if on("sirf") != (on("tsip") or on("garmin") or on("evermore")):
- return False
- #
- # END OF FILTERS
- return True
- def run(self, generator, limit=cyclesize):
- expect = 0
- for i in range(cyclesize):
- if self.filter(self.n2v(i)):
- expect += 1
- print("# Expect %d of %d tests." % (expect, cyclesize))
- starttime = time.time()
- included = 0
- excluded = 0
- for n in generator:
- if limit == 0:
- return
- vector = self.n2v(n)
- if not self.filter(vector):
- print("EXCLUDED:", self.test_header(vector))
- excluded += 1
- else:
- self.configure_build(vector)
- included += 1
- limit -= 1
- elapsed = int(time.time() - starttime)
- hours = elapsed/3600
- minutes = (elapsed - (hours * 3600)) / 60
- seconds = elapsed - (hours * 3600) - (minutes * 60)
- print("%d tests, %d excluded, in %dh%dm%ds" %
- (included, excluded, hours, minutes, seconds))
- if __name__ == '__main__':
- try:
- (options, arguments) = getopt.getopt(sys.argv[1:], "n:r:")
- except getopt.GetoptError, msg:
- sys.exit("regress-builder: " + str(msg))
- montecarlo = False
- limit = cyclesize
- seed = int(time.time())
- for (switch, val) in options:
- if switch == '-r':
- montecarlo = True
- limit = int(val)
- elif switch == '-s':
- montecarlo = True
- seed = int(val)
- if montecarlo:
- print("Monte Carlo test seeded with %d" % seed)
- TestFactory().run(MonteCarlo(seed), limit)
- else:
- print("Sequential compilation test")
- TestFactory().run(Sequential())
|