Open Media Library Platform
This commit is contained in:
commit
411ad5b16f
5849 changed files with 1778641 additions and 0 deletions
|
|
@ -0,0 +1,5 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
"""
|
||||
Tests for the Twisted positioning framework.
|
||||
"""
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
"""
|
||||
Receivers for use in tests.
|
||||
"""
|
||||
from twisted.positioning import base, ipositioning
|
||||
|
||||
|
||||
class MockPositioningReceiver(base.BasePositioningReceiver):
|
||||
"""
|
||||
A mock positioning receiver.
|
||||
|
||||
Mocks all the L{IPositioningReceiver} methods with stubs that don't do
|
||||
anything but register that they were called.
|
||||
|
||||
@ivar called: A mapping of names of callbacks that have been called to
|
||||
C{True}.
|
||||
@type called: C{dict}
|
||||
"""
|
||||
def __init__(self):
|
||||
self.clear()
|
||||
|
||||
for methodName in ipositioning.IPositioningReceiver:
|
||||
self._addCallback(methodName)
|
||||
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
Forget all the methods that have been called on this receiver, by
|
||||
emptying C{self.called}.
|
||||
"""
|
||||
self.called = {}
|
||||
|
||||
|
||||
def _addCallback(self, name):
|
||||
"""
|
||||
Adds a callback of the given name, setting C{self.called[name]} to
|
||||
C{True} when called.
|
||||
"""
|
||||
def callback(*a, **kw):
|
||||
self.called[name] = True
|
||||
|
||||
setattr(self, name, callback)
|
||||
|
|
@ -0,0 +1,917 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
"""
|
||||
Test cases for positioning primitives.
|
||||
"""
|
||||
from twisted.trial.unittest import TestCase
|
||||
from twisted.positioning import base
|
||||
from twisted.positioning.base import Angles, Directions
|
||||
from twisted.positioning.ipositioning import IPositioningBeacon
|
||||
from zope.interface import verify
|
||||
|
||||
|
||||
class AngleTests(TestCase):
|
||||
"""
|
||||
Tests for the L{twisted.positioning.base.Angle} class.
|
||||
"""
|
||||
def test_empty(self):
|
||||
"""
|
||||
The repr of an empty angle says that is of unknown type and unknown
|
||||
value.
|
||||
"""
|
||||
a = base.Angle()
|
||||
self.assertEqual("<Angle of unknown type (unknown value)>", repr(a))
|
||||
|
||||
|
||||
def test_variation(self):
|
||||
"""
|
||||
The repr of an empty variation says that it is a variation of unknown
|
||||
value.
|
||||
"""
|
||||
a = base.Angle(angleType=Angles.VARIATION)
|
||||
self.assertEqual("<Variation (unknown value)>", repr(a))
|
||||
|
||||
|
||||
def test_unknownType(self):
|
||||
"""
|
||||
The repr of an angle of unknown type but a given value displays that
|
||||
type and value in its repr.
|
||||
"""
|
||||
a = base.Angle(1.0)
|
||||
self.assertEqual("<Angle of unknown type (1.0 degrees)>", repr(a))
|
||||
|
||||
|
||||
def test_bogusType(self):
|
||||
"""
|
||||
Trying to create an angle with a bogus type raises C{ValueError}.
|
||||
"""
|
||||
self.assertRaises(ValueError, base.Angle, angleType="BOGUS")
|
||||
|
||||
|
||||
|
||||
class HeadingTests(TestCase):
|
||||
"""
|
||||
Tests for the L{twisted.positioning.base.Heading} class.
|
||||
"""
|
||||
def test_simple(self):
|
||||
"""
|
||||
Tests that a simple heading has a value in decimal degrees, which is
|
||||
also its value when converted to a float. Its variation, and by
|
||||
consequence its corrected heading, is C{None}.
|
||||
"""
|
||||
h = base.Heading(1.)
|
||||
self.assertEqual(h.inDecimalDegrees, 1.)
|
||||
self.assertEqual(float(h), 1.)
|
||||
self.assertEqual(h.variation, None)
|
||||
self.assertEqual(h.correctedHeading, None)
|
||||
|
||||
|
||||
def test_headingWithoutVariationRepr(self):
|
||||
"""
|
||||
A repr of a heading with no variation reports its value and that the
|
||||
variation is unknown.
|
||||
"""
|
||||
heading = base.Heading(1.)
|
||||
expectedRepr = "<Heading (1.0 degrees, unknown variation)>"
|
||||
self.assertEqual(repr(heading), expectedRepr)
|
||||
|
||||
|
||||
def test_headingWithVariationRepr(self):
|
||||
"""
|
||||
A repr of a heading with known variation reports its value and the
|
||||
value of that variation.
|
||||
"""
|
||||
angle, variation = 1.0, -10.0
|
||||
heading = base.Heading.fromFloats(angle, variationValue=variation)
|
||||
reprTemplate = '<Heading ({0} degrees, <Variation ({1} degrees)>)>'
|
||||
self.assertEqual(repr(heading), reprTemplate.format(angle, variation))
|
||||
|
||||
|
||||
def test_valueEquality(self):
|
||||
"""
|
||||
Headings with the same values compare equal.
|
||||
"""
|
||||
self.assertEqual(base.Heading(1.), base.Heading(1.))
|
||||
|
||||
|
||||
def test_valueInequality(self):
|
||||
"""
|
||||
Headings with different values compare unequal.
|
||||
"""
|
||||
self.assertNotEquals(base.Heading(1.), base.Heading(2.))
|
||||
|
||||
|
||||
def test_zeroHeadingEdgeCase(self):
|
||||
"""
|
||||
Headings can be instantiated with a value of 0 and no variation.
|
||||
"""
|
||||
base.Heading(0)
|
||||
|
||||
|
||||
def test_zeroHeading180DegreeVariationEdgeCase(self):
|
||||
"""
|
||||
Headings can be instantiated with a value of 0 and a variation of 180
|
||||
degrees.
|
||||
"""
|
||||
base.Heading(0, 180)
|
||||
|
||||
|
||||
def _badValueTest(self, **kw):
|
||||
"""
|
||||
Helper function for verifying that bad values raise C{ValueError}.
|
||||
|
||||
@param kw: The keyword arguments passed to L{base.Heading.fromFloats}.
|
||||
"""
|
||||
self.assertRaises(ValueError, base.Heading.fromFloats, **kw)
|
||||
|
||||
|
||||
def test_badAngleValueEdgeCase(self):
|
||||
"""
|
||||
Headings can not be instantiated with a value of 360 degrees.
|
||||
"""
|
||||
self._badValueTest(angleValue=360.0)
|
||||
|
||||
|
||||
def test_badVariationEdgeCase(self):
|
||||
"""
|
||||
Headings can not be instantiated with a variation of -180 degrees.
|
||||
"""
|
||||
self._badValueTest(variationValue=-180.0)
|
||||
|
||||
|
||||
def test_negativeHeading(self):
|
||||
"""
|
||||
Negative heading values raise C{ValueError}.
|
||||
"""
|
||||
self._badValueTest(angleValue=-10.0)
|
||||
|
||||
|
||||
def test_headingTooLarge(self):
|
||||
"""
|
||||
Heading values greater than C{360.0} raise C{ValueError}.
|
||||
"""
|
||||
self._badValueTest(angleValue=370.0)
|
||||
|
||||
|
||||
def test_variationTooNegative(self):
|
||||
"""
|
||||
Variation values less than C{-180.0} raise C{ValueError}.
|
||||
"""
|
||||
self._badValueTest(variationValue=-190.0)
|
||||
|
||||
|
||||
def test_variationTooPositive(self):
|
||||
"""
|
||||
Variation values greater than C{180.0} raise C{ValueError}.
|
||||
"""
|
||||
self._badValueTest(variationValue=190.0)
|
||||
|
||||
|
||||
def test_correctedHeading(self):
|
||||
"""
|
||||
A heading with a value and a variation has a corrected heading.
|
||||
"""
|
||||
h = base.Heading.fromFloats(1., variationValue=-10.)
|
||||
self.assertEqual(h.correctedHeading, base.Angle(11., Angles.HEADING))
|
||||
|
||||
|
||||
def test_correctedHeadingOverflow(self):
|
||||
"""
|
||||
A heading with a value and a variation has the appropriate corrected
|
||||
heading value, even when the variation puts it across the 360 degree
|
||||
boundary.
|
||||
"""
|
||||
h = base.Heading.fromFloats(359., variationValue=-2.)
|
||||
self.assertEqual(h.correctedHeading, base.Angle(1., Angles.HEADING))
|
||||
|
||||
|
||||
def test_correctedHeadingOverflowEdgeCase(self):
|
||||
"""
|
||||
A heading with a value and a variation has the appropriate corrected
|
||||
heading value, even when the variation puts it exactly at the 360
|
||||
degree boundary.
|
||||
"""
|
||||
h = base.Heading.fromFloats(359., variationValue=-1.)
|
||||
self.assertEqual(h.correctedHeading, base.Angle(0., Angles.HEADING))
|
||||
|
||||
|
||||
def test_correctedHeadingUnderflow(self):
|
||||
"""
|
||||
A heading with a value and a variation has the appropriate corrected
|
||||
heading value, even when the variation puts it under the 0 degree
|
||||
boundary.
|
||||
"""
|
||||
h = base.Heading.fromFloats(1., variationValue=2.)
|
||||
self.assertEqual(h.correctedHeading, base.Angle(359., Angles.HEADING))
|
||||
|
||||
|
||||
def test_correctedHeadingUnderflowEdgeCase(self):
|
||||
"""
|
||||
A heading with a value and a variation has the appropriate corrected
|
||||
heading value, even when the variation puts it exactly at the 0
|
||||
degree boundary.
|
||||
"""
|
||||
h = base.Heading.fromFloats(1., variationValue=1.)
|
||||
self.assertEqual(h.correctedHeading, base.Angle(0., Angles.HEADING))
|
||||
|
||||
|
||||
def test_setVariationSign(self):
|
||||
"""
|
||||
Setting the sign of a heading changes the variation sign.
|
||||
"""
|
||||
h = base.Heading.fromFloats(1., variationValue=1.)
|
||||
h.setSign(1)
|
||||
self.assertEqual(h.variation.inDecimalDegrees, 1.)
|
||||
h.setSign(-1)
|
||||
self.assertEqual(h.variation.inDecimalDegrees, -1.)
|
||||
|
||||
|
||||
def test_setBadVariationSign(self):
|
||||
"""
|
||||
Setting the sign of a heading to values that aren't C{-1} or C{1}
|
||||
raises C{ValueError} and does not affect the heading.
|
||||
"""
|
||||
h = base.Heading.fromFloats(1., variationValue=1.)
|
||||
self.assertRaises(ValueError, h.setSign, -50)
|
||||
self.assertEqual(h.variation.inDecimalDegrees, 1.)
|
||||
|
||||
self.assertRaises(ValueError, h.setSign, 0)
|
||||
self.assertEqual(h.variation.inDecimalDegrees, 1.)
|
||||
|
||||
self.assertRaises(ValueError, h.setSign, 50)
|
||||
self.assertEqual(h.variation.inDecimalDegrees, 1.)
|
||||
|
||||
|
||||
def test_setUnknownVariationSign(self):
|
||||
"""
|
||||
Setting the sign on a heading with unknown variation raises
|
||||
C{ValueError}.
|
||||
"""
|
||||
h = base.Heading.fromFloats(1.)
|
||||
self.assertIdentical(None, h.variation.inDecimalDegrees)
|
||||
self.assertRaises(ValueError, h.setSign, 1)
|
||||
|
||||
|
||||
|
||||
class CoordinateTests(TestCase):
|
||||
def test_float(self):
|
||||
"""
|
||||
Coordinates can be converted to floats.
|
||||
"""
|
||||
coordinate = base.Coordinate(10.0)
|
||||
self.assertEqual(float(coordinate), 10.0)
|
||||
|
||||
|
||||
def test_repr(self):
|
||||
"""
|
||||
Coordinates that aren't explicitly latitudes or longitudes have an
|
||||
appropriate repr.
|
||||
"""
|
||||
coordinate = base.Coordinate(10.0)
|
||||
expectedRepr = "<Angle of unknown type ({0} degrees)>".format(10.0)
|
||||
self.assertEqual(repr(coordinate), expectedRepr)
|
||||
|
||||
|
||||
def test_positiveLatitude(self):
|
||||
"""
|
||||
Positive latitudes have a repr that specifies their type and value.
|
||||
"""
|
||||
coordinate = base.Coordinate(10.0, Angles.LATITUDE)
|
||||
expectedRepr = "<Latitude ({0} degrees)>".format(10.0)
|
||||
self.assertEqual(repr(coordinate), expectedRepr)
|
||||
|
||||
|
||||
def test_negativeLatitude(self):
|
||||
"""
|
||||
Negative latitudes have a repr that specifies their type and value.
|
||||
"""
|
||||
coordinate = base.Coordinate(-50.0, Angles.LATITUDE)
|
||||
expectedRepr = "<Latitude ({0} degrees)>".format(-50.0)
|
||||
self.assertEqual(repr(coordinate), expectedRepr)
|
||||
|
||||
|
||||
def test_positiveLongitude(self):
|
||||
"""
|
||||
Positive longitudes have a repr that specifies their type and value.
|
||||
"""
|
||||
longitude = base.Coordinate(50.0, Angles.LONGITUDE)
|
||||
expectedRepr = "<Longitude ({0} degrees)>".format(50.0)
|
||||
self.assertEqual(repr(longitude), expectedRepr)
|
||||
|
||||
|
||||
def test_negativeLongitude(self):
|
||||
"""
|
||||
Negative longitudes have a repr that specifies their type and value.
|
||||
"""
|
||||
longitude = base.Coordinate(-50.0, Angles.LONGITUDE)
|
||||
expectedRepr = "<Longitude ({0} degrees)>".format(-50.0)
|
||||
self.assertEqual(repr(longitude), expectedRepr)
|
||||
|
||||
|
||||
def test_bogusCoordinateType(self):
|
||||
"""
|
||||
Creating coordinates with bogus types rasies C{ValueError}.
|
||||
"""
|
||||
self.assertRaises(ValueError, base.Coordinate, 150.0, "BOGUS")
|
||||
|
||||
|
||||
def test_angleTypeNotCoordinate(self):
|
||||
"""
|
||||
Creating coordinates with angle types that aren't coordinates raises
|
||||
C{ValueError}.
|
||||
"""
|
||||
self.assertRaises(ValueError, base.Coordinate, 150.0, Angles.HEADING)
|
||||
|
||||
|
||||
def test_equality(self):
|
||||
"""
|
||||
Coordinates with the same value and type are equal.
|
||||
"""
|
||||
def makeCoordinate():
|
||||
return base.Coordinate(1.0, Angles.LONGITUDE)
|
||||
self.assertEqual(makeCoordinate(), makeCoordinate())
|
||||
|
||||
|
||||
def test_differentAnglesInequality(self):
|
||||
"""
|
||||
Coordinates with different values aren't equal.
|
||||
"""
|
||||
c1 = base.Coordinate(1.0)
|
||||
c2 = base.Coordinate(-1.0)
|
||||
self.assertNotEqual(c1, c2)
|
||||
|
||||
|
||||
def test_differentTypesInequality(self):
|
||||
"""
|
||||
Coordinates with the same values but different types aren't equal.
|
||||
"""
|
||||
c1 = base.Coordinate(1.0, Angles.LATITUDE)
|
||||
c2 = base.Coordinate(1.0, Angles.LONGITUDE)
|
||||
self.assertNotEqual(c1, c2)
|
||||
|
||||
|
||||
def test_sign(self):
|
||||
"""
|
||||
Setting the sign on a coordinate sets the sign of the value of the
|
||||
coordinate.
|
||||
"""
|
||||
c = base.Coordinate(50., Angles.LATITUDE)
|
||||
c.setSign(1)
|
||||
self.assertEqual(c.inDecimalDegrees, 50.)
|
||||
c.setSign(-1)
|
||||
self.assertEqual(c.inDecimalDegrees, -50.)
|
||||
|
||||
|
||||
def test_badVariationSign(self):
|
||||
"""
|
||||
Setting a bogus sign value (not -1 or 1) on a coordinate raises
|
||||
C{ValueError} and doesn't affect the coordinate.
|
||||
"""
|
||||
value = 50.0
|
||||
c = base.Coordinate(value, Angles.LATITUDE)
|
||||
|
||||
self.assertRaises(ValueError, c.setSign, -50)
|
||||
self.assertEqual(c.inDecimalDegrees, 50.)
|
||||
|
||||
self.assertRaises(ValueError, c.setSign, 0)
|
||||
self.assertEqual(c.inDecimalDegrees, 50.)
|
||||
|
||||
self.assertRaises(ValueError, c.setSign, 50)
|
||||
self.assertEqual(c.inDecimalDegrees, 50.)
|
||||
|
||||
|
||||
def test_northernHemisphere(self):
|
||||
"""
|
||||
Positive latitudes are in the northern hemisphere.
|
||||
"""
|
||||
coordinate = base.Coordinate(1.0, Angles.LATITUDE)
|
||||
self.assertEqual(coordinate.hemisphere, Directions.NORTH)
|
||||
|
||||
|
||||
def test_easternHemisphere(self):
|
||||
"""
|
||||
Positive longitudes are in the eastern hemisphere.
|
||||
"""
|
||||
coordinate = base.Coordinate(1.0, Angles.LONGITUDE)
|
||||
self.assertEqual(coordinate.hemisphere, Directions.EAST)
|
||||
|
||||
|
||||
def test_southernHemisphere(self):
|
||||
"""
|
||||
Negative latitudes are in the southern hemisphere.
|
||||
"""
|
||||
coordinate = base.Coordinate(-1.0, Angles.LATITUDE)
|
||||
self.assertEqual(coordinate.hemisphere, Directions.SOUTH)
|
||||
|
||||
|
||||
def test_westernHemisphere(self):
|
||||
"""
|
||||
Negative longitudes are in the western hemisphere.
|
||||
"""
|
||||
coordinate = base.Coordinate(-1.0, Angles.LONGITUDE)
|
||||
self.assertEqual(coordinate.hemisphere, Directions.WEST)
|
||||
|
||||
|
||||
def test_badHemisphere(self):
|
||||
"""
|
||||
Accessing the hemisphere for a coordinate that can't compute it
|
||||
raises C{ValueError}.
|
||||
"""
|
||||
coordinate = base.Coordinate(1.0, None)
|
||||
self.assertRaises(ValueError, lambda: coordinate.hemisphere)
|
||||
|
||||
|
||||
def test_latitudeTooLarge(self):
|
||||
"""
|
||||
Creating a latitude with a value greater than or equal to 90 degrees
|
||||
raises C{ValueError}.
|
||||
"""
|
||||
self.assertRaises(ValueError, _makeLatitude, 150.0)
|
||||
self.assertRaises(ValueError, _makeLatitude, 90.0)
|
||||
|
||||
|
||||
def test_latitudeTooSmall(self):
|
||||
"""
|
||||
Creating a latitude with a value less than or equal to -90 degrees
|
||||
raises C{ValueError}.
|
||||
"""
|
||||
self.assertRaises(ValueError, _makeLatitude, -150.0)
|
||||
self.assertRaises(ValueError, _makeLatitude, -90.0)
|
||||
|
||||
|
||||
def test_longitudeTooLarge(self):
|
||||
"""
|
||||
Creating a longitude with a value greater than or equal to 180 degrees
|
||||
raises C{ValueError}.
|
||||
"""
|
||||
self.assertRaises(ValueError, _makeLongitude, 250.0)
|
||||
self.assertRaises(ValueError, _makeLongitude, 180.0)
|
||||
|
||||
|
||||
def test_longitudeTooSmall(self):
|
||||
"""
|
||||
Creating a longitude with a value less than or equal to -180 degrees
|
||||
raises C{ValueError}.
|
||||
"""
|
||||
self.assertRaises(ValueError, _makeLongitude, -250.0)
|
||||
self.assertRaises(ValueError, _makeLongitude, -180.0)
|
||||
|
||||
|
||||
def test_inDegreesMinutesSeconds(self):
|
||||
"""
|
||||
Coordinate values can be accessed in degrees, minutes, seconds.
|
||||
"""
|
||||
c = base.Coordinate(50.5, Angles.LATITUDE)
|
||||
self.assertEqual(c.inDegreesMinutesSeconds, (50, 30, 0))
|
||||
|
||||
c = base.Coordinate(50.213, Angles.LATITUDE)
|
||||
self.assertEqual(c.inDegreesMinutesSeconds, (50, 12, 46))
|
||||
|
||||
|
||||
def test_unknownAngleInDegreesMinutesSeconds(self):
|
||||
"""
|
||||
If the vaue of a coordinate is C{None}, its values in degrees,
|
||||
minutes, seconds is also C{None}.
|
||||
"""
|
||||
c = base.Coordinate(None, None)
|
||||
self.assertEqual(c.inDegreesMinutesSeconds, None)
|
||||
|
||||
|
||||
|
||||
def _makeLatitude(value):
|
||||
"""
|
||||
Builds and returns a latitude of given value.
|
||||
"""
|
||||
return base.Coordinate(value, Angles.LATITUDE)
|
||||
|
||||
|
||||
|
||||
def _makeLongitude(value):
|
||||
"""
|
||||
Builds and returns a longitude of given value.
|
||||
"""
|
||||
return base.Coordinate(value, Angles.LONGITUDE)
|
||||
|
||||
|
||||
|
||||
class AltitudeTests(TestCase):
|
||||
"""
|
||||
Tests for the L{twisted.positioning.base.Altitude} class.
|
||||
"""
|
||||
def test_value(self):
|
||||
"""
|
||||
Altitudes can be instantiated and reports the correct value in
|
||||
meters and feet, as well as when converted to float.
|
||||
"""
|
||||
altitude = base.Altitude(1.)
|
||||
self.assertEqual(float(altitude), 1.)
|
||||
self.assertEqual(altitude.inMeters, 1.)
|
||||
self.assertEqual(altitude.inFeet, 1./base.METERS_PER_FOOT)
|
||||
|
||||
|
||||
def test_repr(self):
|
||||
"""
|
||||
Altitudes report their type and value in their repr.
|
||||
"""
|
||||
altitude = base.Altitude(1.)
|
||||
self.assertEqual(repr(altitude), "<Altitude (1.0 m)>")
|
||||
|
||||
|
||||
def test_equality(self):
|
||||
"""
|
||||
Altitudes with equal values compare equal.
|
||||
"""
|
||||
firstAltitude = base.Altitude(1.)
|
||||
secondAltitude = base.Altitude(1.)
|
||||
self.assertEqual(firstAltitude, secondAltitude)
|
||||
|
||||
|
||||
def test_inequality(self):
|
||||
"""
|
||||
Altitudes with different values don't compare equal.
|
||||
"""
|
||||
firstAltitude = base.Altitude(1.)
|
||||
secondAltitude = base.Altitude(-1.)
|
||||
self.assertNotEquals(firstAltitude, secondAltitude)
|
||||
|
||||
|
||||
|
||||
class SpeedTests(TestCase):
|
||||
"""
|
||||
Tests for the L{twisted.positioning.base.Speed} class.
|
||||
"""
|
||||
def test_value(self):
|
||||
"""
|
||||
Speeds can be instantiated, and report their value in meters
|
||||
per second, and can be converted to floats.
|
||||
"""
|
||||
speed = base.Speed(50.0)
|
||||
self.assertEqual(speed.inMetersPerSecond, 50.0)
|
||||
self.assertEqual(float(speed), 50.0)
|
||||
|
||||
|
||||
def test_repr(self):
|
||||
"""
|
||||
Speeds report their type and value in their repr.
|
||||
"""
|
||||
speed = base.Speed(50.0)
|
||||
self.assertEqual(repr(speed), "<Speed (50.0 m/s)>")
|
||||
|
||||
|
||||
def test_negativeSpeeds(self):
|
||||
"""
|
||||
Creating a negative speed raises C{ValueError}.
|
||||
"""
|
||||
self.assertRaises(ValueError, base.Speed, -1.0)
|
||||
|
||||
|
||||
def test_inKnots(self):
|
||||
"""
|
||||
A speed can be converted into its value in knots.
|
||||
"""
|
||||
speed = base.Speed(1.0)
|
||||
self.assertEqual(1/base.MPS_PER_KNOT, speed.inKnots)
|
||||
|
||||
|
||||
def test_asFloat(self):
|
||||
"""
|
||||
A speed can be converted into a C{float}.
|
||||
"""
|
||||
self.assertEqual(1.0, float(base.Speed(1.0)))
|
||||
|
||||
|
||||
|
||||
class ClimbTests(TestCase):
|
||||
"""
|
||||
Tests for L{twisted.positioning.base.Climb}.
|
||||
"""
|
||||
def test_simple(self):
|
||||
"""
|
||||
Speeds can be instantiated, and report their value in meters
|
||||
per second, and can be converted to floats.
|
||||
"""
|
||||
climb = base.Climb(42.)
|
||||
self.assertEqual(climb.inMetersPerSecond, 42.)
|
||||
self.assertEqual(float(climb), 42.)
|
||||
|
||||
|
||||
def test_repr(self):
|
||||
"""
|
||||
Climbs report their type and value in their repr.
|
||||
"""
|
||||
climb = base.Climb(42.)
|
||||
self.assertEqual(repr(climb), "<Climb (42.0 m/s)>")
|
||||
|
||||
|
||||
def test_negativeClimbs(self):
|
||||
"""
|
||||
Climbs can have negative values, and still report that value
|
||||
in meters per second and when converted to floats.
|
||||
"""
|
||||
climb = base.Climb(-42.)
|
||||
self.assertEqual(climb.inMetersPerSecond, -42.)
|
||||
self.assertEqual(float(climb), -42.)
|
||||
|
||||
|
||||
def test_speedInKnots(self):
|
||||
"""
|
||||
A climb can be converted into its value in knots.
|
||||
"""
|
||||
climb = base.Climb(1.0)
|
||||
self.assertEqual(1/base.MPS_PER_KNOT, climb.inKnots)
|
||||
|
||||
|
||||
def test_asFloat(self):
|
||||
"""
|
||||
A climb can be converted into a C{float}.
|
||||
"""
|
||||
self.assertEqual(1.0, float(base.Climb(1.0)))
|
||||
|
||||
|
||||
|
||||
class PositionErrorTests(TestCase):
|
||||
"""
|
||||
Tests for L{twisted.positioning.base.PositionError}.
|
||||
"""
|
||||
def test_allUnset(self):
|
||||
"""
|
||||
In an empty L{base.PositionError} with no invariant testing, all
|
||||
dilutions of positions are C{None}.
|
||||
"""
|
||||
positionError = base.PositionError()
|
||||
self.assertEqual(positionError.pdop, None)
|
||||
self.assertEqual(positionError.hdop, None)
|
||||
self.assertEqual(positionError.vdop, None)
|
||||
|
||||
|
||||
def test_allUnsetWithInvariant(self):
|
||||
"""
|
||||
In an empty L{base.PositionError} with invariant testing, all
|
||||
dilutions of positions are C{None}.
|
||||
"""
|
||||
positionError = base.PositionError(testInvariant=True)
|
||||
self.assertEqual(positionError.pdop, None)
|
||||
self.assertEqual(positionError.hdop, None)
|
||||
self.assertEqual(positionError.vdop, None)
|
||||
|
||||
|
||||
def test_withoutInvariant(self):
|
||||
"""
|
||||
L{base.PositionError}s can be instantiated with just a HDOP.
|
||||
"""
|
||||
positionError = base.PositionError(hdop=1.0)
|
||||
self.assertEqual(positionError.hdop, 1.0)
|
||||
|
||||
|
||||
def test_withInvariant(self):
|
||||
"""
|
||||
Creating a simple L{base.PositionError} with just a HDOP while
|
||||
checking the invariant works.
|
||||
"""
|
||||
positionError = base.PositionError(hdop=1.0, testInvariant=True)
|
||||
self.assertEqual(positionError.hdop, 1.0)
|
||||
|
||||
|
||||
def test_invalidWithoutInvariant(self):
|
||||
"""
|
||||
Creating a L{base.PositionError} with values set to an impossible
|
||||
combination works if the invariant is not checked.
|
||||
"""
|
||||
error = base.PositionError(pdop=1.0, vdop=1.0, hdop=1.0)
|
||||
self.assertEqual(error.pdop, 1.0)
|
||||
self.assertEqual(error.hdop, 1.0)
|
||||
self.assertEqual(error.vdop, 1.0)
|
||||
|
||||
|
||||
def test_invalidWithInvariant(self):
|
||||
"""
|
||||
Creating a L{base.PositionError} with values set to an impossible
|
||||
combination raises C{ValueError} if the invariant is being tested.
|
||||
"""
|
||||
self.assertRaises(ValueError, base.PositionError,
|
||||
pdop=1.0, vdop=1.0, hdop=1.0, testInvariant=True)
|
||||
|
||||
|
||||
def test_setDOPWithoutInvariant(self):
|
||||
"""
|
||||
You can set the PDOP value to value inconsisted with HDOP and VDOP
|
||||
when not checking the invariant.
|
||||
"""
|
||||
pe = base.PositionError(hdop=1.0, vdop=1.0)
|
||||
pe.pdop = 100.0
|
||||
self.assertEqual(pe.pdop, 100.0)
|
||||
|
||||
|
||||
def test_setDOPWithInvariant(self):
|
||||
"""
|
||||
Attempting to set the PDOP value to value inconsisted with HDOP and
|
||||
VDOP when checking the invariant raises C{ValueError}.
|
||||
"""
|
||||
pe = base.PositionError(hdop=1.0, vdop=1.0, testInvariant=True)
|
||||
pdop = pe.pdop
|
||||
|
||||
def setPDOP(pe):
|
||||
pe.pdop = 100.0
|
||||
|
||||
self.assertRaises(ValueError, setPDOP, pe)
|
||||
self.assertEqual(pe.pdop, pdop)
|
||||
|
||||
|
||||
REPR_TEMPLATE = "<PositionError (pdop: %s, hdop: %s, vdop: %s)>"
|
||||
|
||||
|
||||
def _testDOP(self, pe, pdop, hdop, vdop):
|
||||
"""
|
||||
Tests the DOP values in a position error, and the repr of that
|
||||
position error.
|
||||
|
||||
@param pe: The position error under test.
|
||||
@type pe: C{PositionError}
|
||||
@param pdop: The expected position dilution of precision.
|
||||
@type pdop: C{float} or C{NoneType}
|
||||
@param hdop: The expected horizontal dilution of precision.
|
||||
@type hdop: C{float} or C{NoneType}
|
||||
@param vdop: The expected vertical dilution of precision.
|
||||
@type vdop: C{float} or C{NoneType}
|
||||
"""
|
||||
self.assertEqual(pe.pdop, pdop)
|
||||
self.assertEqual(pe.hdop, hdop)
|
||||
self.assertEqual(pe.vdop, vdop)
|
||||
self.assertEqual(repr(pe), self.REPR_TEMPLATE % (pdop, hdop, vdop))
|
||||
|
||||
|
||||
def test_positionAndHorizontalSet(self):
|
||||
"""
|
||||
The VDOP is correctly determined from PDOP and HDOP.
|
||||
"""
|
||||
pdop, hdop = 2.0, 1.0
|
||||
vdop = (pdop**2 - hdop**2)**.5
|
||||
pe = base.PositionError(pdop=pdop, hdop=hdop)
|
||||
self._testDOP(pe, pdop, hdop, vdop)
|
||||
|
||||
|
||||
def test_positionAndVerticalSet(self):
|
||||
"""
|
||||
The HDOP is correctly determined from PDOP and VDOP.
|
||||
"""
|
||||
pdop, vdop = 2.0, 1.0
|
||||
hdop = (pdop**2 - vdop**2)**.5
|
||||
pe = base.PositionError(pdop=pdop, vdop=vdop)
|
||||
self._testDOP(pe, pdop, hdop, vdop)
|
||||
|
||||
|
||||
def test_horizontalAndVerticalSet(self):
|
||||
"""
|
||||
The PDOP is correctly determined from HDOP and VDOP.
|
||||
"""
|
||||
hdop, vdop = 1.0, 1.0
|
||||
pdop = (hdop**2 + vdop**2)**.5
|
||||
pe = base.PositionError(hdop=hdop, vdop=vdop)
|
||||
self._testDOP(pe, pdop, hdop, vdop)
|
||||
|
||||
|
||||
|
||||
class BeaconInformationTests(TestCase):
|
||||
"""
|
||||
Tests for L{twisted.positioning.base.BeaconInformation}.
|
||||
"""
|
||||
def test_minimal(self):
|
||||
"""
|
||||
For an empty beacon information object, the number of used
|
||||
beacons is zero, the number of seen beacons is zero, and the
|
||||
repr of the object reflects that.
|
||||
"""
|
||||
bi = base.BeaconInformation()
|
||||
self.assertEqual(len(bi.usedBeacons), 0)
|
||||
expectedRepr = ("<BeaconInformation ("
|
||||
"used beacons (0): [], "
|
||||
"unused beacons: [])>")
|
||||
self.assertEqual(repr(bi), expectedRepr)
|
||||
|
||||
|
||||
satelliteKwargs = {"azimuth": 1, "elevation": 1, "signalToNoiseRatio": 1.}
|
||||
|
||||
|
||||
def test_simple(self):
|
||||
"""
|
||||
Tests a beacon information with a bunch of satellites, none of
|
||||
which used in computing a fix.
|
||||
"""
|
||||
def _buildSatellite(**kw):
|
||||
kwargs = dict(self.satelliteKwargs)
|
||||
kwargs.update(kw)
|
||||
return base.Satellite(**kwargs)
|
||||
|
||||
beacons = set()
|
||||
for prn in range(1, 10):
|
||||
beacons.add(_buildSatellite(identifier=prn))
|
||||
|
||||
bi = base.BeaconInformation(beacons)
|
||||
|
||||
self.assertEqual(len(bi.seenBeacons), 9)
|
||||
self.assertEqual(len(bi.usedBeacons), 0)
|
||||
self.assertEqual(repr(bi),
|
||||
"<BeaconInformation (used beacons (0): [], "
|
||||
"unused beacons: ["
|
||||
"<Satellite (1), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (2), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (3), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (4), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (5), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (6), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (7), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (8), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (9), azimuth: 1, elevation: 1, snr: 1.0>"
|
||||
"])>")
|
||||
|
||||
|
||||
def test_someSatellitesUsed(self):
|
||||
"""
|
||||
Tests a beacon information with a bunch of satellites, some of
|
||||
them used in computing a fix.
|
||||
"""
|
||||
bi = base.BeaconInformation()
|
||||
|
||||
for prn in range(1, 10):
|
||||
satellite = base.Satellite(identifier=prn, **self.satelliteKwargs)
|
||||
bi.seenBeacons.add(satellite)
|
||||
if prn % 2:
|
||||
bi.usedBeacons.add(satellite)
|
||||
|
||||
self.assertEqual(len(bi.seenBeacons), 9)
|
||||
self.assertEqual(len(bi.usedBeacons), 5)
|
||||
|
||||
self.assertEqual(repr(bi),
|
||||
"<BeaconInformation (used beacons (5): ["
|
||||
"<Satellite (1), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (3), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (5), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (7), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (9), azimuth: 1, elevation: 1, snr: 1.0>], "
|
||||
"unused beacons: ["
|
||||
"<Satellite (2), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (4), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (6), azimuth: 1, elevation: 1, snr: 1.0>, "
|
||||
"<Satellite (8), azimuth: 1, elevation: 1, snr: 1.0>])>")
|
||||
|
||||
|
||||
|
||||
class PositioningBeaconTests(TestCase):
|
||||
"""
|
||||
Tests for L{base.PositioningBeacon}.
|
||||
"""
|
||||
def test_interface(self):
|
||||
"""
|
||||
Tests that L{base.PositioningBeacon} implements L{IPositioningBeacon}.
|
||||
"""
|
||||
implements = IPositioningBeacon.implementedBy(base.PositioningBeacon)
|
||||
self.assertTrue(implements)
|
||||
verify.verifyObject(IPositioningBeacon, base.PositioningBeacon(1))
|
||||
|
||||
|
||||
def test_repr(self):
|
||||
"""
|
||||
Tests the repr of a positioning beacon.
|
||||
"""
|
||||
self.assertEqual(repr(base.PositioningBeacon("A")), "<Beacon (A)>")
|
||||
|
||||
|
||||
|
||||
class SatelliteTests(TestCase):
|
||||
"""
|
||||
Tests for L{twisted.positioning.base.Satellite}.
|
||||
"""
|
||||
def test_minimal(self):
|
||||
"""
|
||||
Tests a minimal satellite that only has a known PRN.
|
||||
|
||||
Tests that the azimuth, elevation and signal to noise ratios
|
||||
are C{None} and verifies the repr.
|
||||
"""
|
||||
s = base.Satellite(1)
|
||||
self.assertEqual(s.identifier, 1)
|
||||
self.assertEqual(s.azimuth, None)
|
||||
self.assertEqual(s.elevation, None)
|
||||
self.assertEqual(s.signalToNoiseRatio, None)
|
||||
self.assertEqual(repr(s), "<Satellite (1), azimuth: None, "
|
||||
"elevation: None, snr: None>")
|
||||
|
||||
|
||||
def test_simple(self):
|
||||
"""
|
||||
Tests a minimal satellite that only has a known PRN.
|
||||
|
||||
Tests that the azimuth, elevation and signal to noise ratios
|
||||
are correct and verifies the repr.
|
||||
"""
|
||||
s = base.Satellite(identifier=1,
|
||||
azimuth=270.,
|
||||
elevation=30.,
|
||||
signalToNoiseRatio=25.)
|
||||
|
||||
self.assertEqual(s.identifier, 1)
|
||||
self.assertEqual(s.azimuth, 270.)
|
||||
self.assertEqual(s.elevation, 30.)
|
||||
self.assertEqual(s.signalToNoiseRatio, 25.)
|
||||
self.assertEqual(repr(s), "<Satellite (1), azimuth: 270.0, "
|
||||
"elevation: 30.0, snr: 25.0>")
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,166 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
"""
|
||||
Tests for positioning sentences.
|
||||
"""
|
||||
import itertools
|
||||
|
||||
from twisted.positioning import _sentence
|
||||
from twisted.trial.unittest import TestCase
|
||||
|
||||
|
||||
sentinelValueOne = "someStringValue"
|
||||
sentinelValueTwo = "someOtherStringValue"
|
||||
|
||||
|
||||
|
||||
class DummyProtocol(object):
|
||||
"""
|
||||
A simple, fake protocol.
|
||||
"""
|
||||
@staticmethod
|
||||
def getSentenceAttributes():
|
||||
return ["type", sentinelValueOne, sentinelValueTwo]
|
||||
|
||||
|
||||
|
||||
class DummySentence(_sentence._BaseSentence):
|
||||
"""
|
||||
A sentence for L{DummyProtocol}.
|
||||
"""
|
||||
ALLOWED_ATTRIBUTES = DummyProtocol.getSentenceAttributes()
|
||||
|
||||
|
||||
|
||||
class MixinProtocol(_sentence._PositioningSentenceProducerMixin):
|
||||
"""
|
||||
A simple, fake protocol that declaratively tells you the sentences
|
||||
it produces using L{base.PositioningSentenceProducerMixin}.
|
||||
"""
|
||||
_SENTENCE_CONTENTS = {
|
||||
None: [
|
||||
sentinelValueOne,
|
||||
sentinelValueTwo,
|
||||
None # See MixinTests.test_noNoneInSentenceAttributes
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MixinSentence(_sentence._BaseSentence):
|
||||
"""
|
||||
A sentence for L{MixinProtocol}.
|
||||
"""
|
||||
ALLOWED_ATTRIBUTES = MixinProtocol.getSentenceAttributes()
|
||||
|
||||
|
||||
|
||||
class SentenceTestsMixin(object):
|
||||
"""
|
||||
Tests for positioning protocols and their respective sentences.
|
||||
"""
|
||||
def test_attributeAccess(self):
|
||||
"""
|
||||
A sentence attribute gets the correct value, and accessing an
|
||||
unset attribute (which is specified as being a valid sentence
|
||||
attribute) gets C{None}.
|
||||
"""
|
||||
thisSentinel = object()
|
||||
sentence = self.sentenceClass({sentinelValueOne: thisSentinel})
|
||||
self.assertEqual(getattr(sentence, sentinelValueOne), thisSentinel)
|
||||
self.assertEqual(getattr(sentence, sentinelValueTwo), None)
|
||||
|
||||
|
||||
def test_raiseOnMissingAttributeAccess(self):
|
||||
"""
|
||||
Accessing a nonexistant attribute raises C{AttributeError}.
|
||||
"""
|
||||
sentence = self.sentenceClass({})
|
||||
self.assertRaises(AttributeError, getattr, sentence, "BOGUS")
|
||||
|
||||
|
||||
def test_raiseOnBadAttributeAccess(self):
|
||||
"""
|
||||
Accessing bogus attributes raises C{AttributeError}, *even*
|
||||
when that attribute actually is in the sentence data.
|
||||
"""
|
||||
sentence = self.sentenceClass({"BOGUS": None})
|
||||
self.assertRaises(AttributeError, getattr, sentence, "BOGUS")
|
||||
|
||||
|
||||
sentenceType = "tummies"
|
||||
reprTemplate = "<%s (%s) {%s}>"
|
||||
|
||||
|
||||
def _expectedRepr(self, sentenceType="unknown type", dataRepr=""):
|
||||
"""
|
||||
Builds the expected repr for a sentence.
|
||||
|
||||
@param sentenceType: The name of the sentence type (e.g "GPGGA").
|
||||
@type sentenceType: C{str}
|
||||
@param dataRepr: The repr of the data in the sentence.
|
||||
@type dataRepr: C{str}
|
||||
@return: The expected repr of the sentence.
|
||||
@rtype: C{str}
|
||||
"""
|
||||
clsName = self.sentenceClass.__name__
|
||||
return self.reprTemplate % (clsName, sentenceType, dataRepr)
|
||||
|
||||
|
||||
def test_unknownTypeRepr(self):
|
||||
"""
|
||||
Test the repr of an empty sentence of unknown type.
|
||||
"""
|
||||
sentence = self.sentenceClass({})
|
||||
expectedRepr = self._expectedRepr()
|
||||
self.assertEqual(repr(sentence), expectedRepr)
|
||||
|
||||
|
||||
def test_knownTypeRepr(self):
|
||||
"""
|
||||
Test the repr of an empty sentence of known type.
|
||||
"""
|
||||
sentence = self.sentenceClass({"type": self.sentenceType})
|
||||
expectedRepr = self._expectedRepr(self.sentenceType)
|
||||
self.assertEqual(repr(sentence), expectedRepr)
|
||||
|
||||
|
||||
|
||||
class DummyTests(TestCase, SentenceTestsMixin):
|
||||
"""
|
||||
Tests for protocol classes that implement the appropriate interface
|
||||
(L{ipositioning.IPositioningSentenceProducer}) manually.
|
||||
"""
|
||||
def setUp(self):
|
||||
self.protocol = DummyProtocol()
|
||||
self.sentenceClass = DummySentence
|
||||
|
||||
|
||||
|
||||
class MixinTests(TestCase, SentenceTestsMixin):
|
||||
"""
|
||||
Tests for protocols deriving from L{base.PositioningSentenceProducerMixin}
|
||||
and their sentences.
|
||||
"""
|
||||
def setUp(self):
|
||||
self.protocol = MixinProtocol()
|
||||
self.sentenceClass = MixinSentence
|
||||
|
||||
|
||||
def test_noNoneInSentenceAttributes(self):
|
||||
"""
|
||||
C{None} does not appear in the sentence attributes of the
|
||||
protocol, even though it's in the specification.
|
||||
|
||||
This is because C{None} is a placeholder for parts of the sentence you
|
||||
don't really need or want, but there are some bits later on in the
|
||||
sentence that you do want. The alternative would be to have to specify
|
||||
things like "_UNUSED0", "_UNUSED1"... which would end up cluttering
|
||||
the sentence data and eventually adapter state.
|
||||
"""
|
||||
sentenceAttributes = self.protocol.getSentenceAttributes()
|
||||
self.assertNotIn(None, sentenceAttributes)
|
||||
|
||||
sentenceContents = self.protocol._SENTENCE_CONTENTS
|
||||
sentenceSpecAttributes = itertools.chain(*sentenceContents.values())
|
||||
self.assertIn(None, sentenceSpecAttributes)
|
||||
Loading…
Add table
Add a link
Reference in a new issue