예제 #1
0
    def setUp(self):
        """
        Setup test
        """
        try:
            self.bot = WhensMyTrain("whensmytube", testing=self.testing_level)
        except RuntimeError as exc:
            print exc
            self.tearDown()
            self.fail("Sorry, a RuntimeError was encountered")

        self.at_reply = '@%s ' % self.bot.username
        self.geodata_table_names = ('locations', )

        # Regular test data
        #
        # Line, requested stop, latitude, longitude, destination, direction, correct stop name, unwanted destination (if destination or direction specified)
        self.standard_test_data = (
           ('District',             "Earl's Court",  51.4913, -0.1947, "Edgware Road", "Eastbound",   "Earls Ct",      'Wimbledon'),
           ('Victoria',             "Victoria",      51.4966, -0.1448, "Walthamstow",  "Northbound",  "Victoria",      'Brixton'),
           ('Waterloo & City',      "Waterloo",      51.5031, -0.1132, "Bank",         "Eastbound",   "Waterloo",      'Moorgate'),
           ('DLR',                  'Poplar',        51.5077, -0.0174, 'All Saints',   "Northbound",  'Poplar',        'Lewisham'),
           ('Hammersmith and City', "Liverpool St",  51.5186, -0.0813, "Plaistow",     "Eastbound",   "Liverpool St",  'Hammersmith'),
        )
        self.nonstandard_test_data = (
            # Hainault Loop and Northern Line handled correctly
            ("Central Line from White City to Redbridge",
                ("Hainault via Newbury Pk", "Woodford via Hainault"),
                ("Epping [0-9]{4}",)),
            ("Northern Line from Camden Town to Kennington",
                ("via Bank", "via Charing X"),
                ("High Barnet [0-9]{4}",)),
            # Directional sussing at a tricky station
            ("Circle Line from Edgware Road to Moorgate",
                ("Eastbound Train",),
                ("Hammersmith [0-9]{4}",)),
            # Handle ably when no line is specified but only one line serves the origin
            ('Arsenal',
                ('Cockfosters', 'Heathrow'),
                (str(WhensMyTransportException('no_line_specified', 'Arsenal')),)),
            # Handle ably when no line is specified but only one line serves both origin and destination
            ("Earl's Court to Plaistow",
                ('Upminster',),
                (str(WhensMyTransportException('no_line_specified_to', "Earl's Court", "Plaistow")),)),
            # Handle ably when no line is specified, there exists more than one line to get there, but we pick fastest
            ("Stockwell to Euston",
                ('Walthamstow Ctrl',),
                (str(WhensMyTransportException('no_line_specified_to', "Stockwell", "Euston")),)),            
        )
예제 #2
0
class WhensMyTubeTestCase(WhensMyTransportTestCase):
    """
    Main Test Case for When's My Tube
    """
    def setUp(self):
        """
        Setup test
        """
        try:
            self.bot = WhensMyTrain("whensmytube", testing=self.testing_level)
        except RuntimeError as exc:
            print exc
            self.tearDown()
            self.fail("Sorry, a RuntimeError was encountered")

        self.at_reply = '@%s ' % self.bot.username
        self.geodata_table_names = ('locations', )

        # Regular test data
        #
        # Line, requested stop, latitude, longitude, destination, direction, correct stop name, unwanted destination (if destination or direction specified)
        self.standard_test_data = (
           ('District',             "Earl's Court",  51.4913, -0.1947, "Edgware Road", "Eastbound",   "Earls Ct",      'Wimbledon'),
           ('Victoria',             "Victoria",      51.4966, -0.1448, "Walthamstow",  "Northbound",  "Victoria",      'Brixton'),
           ('Waterloo & City',      "Waterloo",      51.5031, -0.1132, "Bank",         "Eastbound",   "Waterloo",      'Moorgate'),
           ('DLR',                  'Poplar',        51.5077, -0.0174, 'All Saints',   "Northbound",  'Poplar',        'Lewisham'),
           ('Hammersmith and City', "Liverpool St",  51.5186, -0.0813, "Plaistow",     "Eastbound",   "Liverpool St",  'Hammersmith'),
        )
        self.nonstandard_test_data = (
            # Hainault Loop and Northern Line handled correctly
            ("Central Line from White City to Redbridge",
                ("Hainault via Newbury Pk", "Woodford via Hainault"),
                ("Epping [0-9]{4}",)),
            ("Northern Line from Camden Town to Kennington",
                ("via Bank", "via Charing X"),
                ("High Barnet [0-9]{4}",)),
            # Directional sussing at a tricky station
            ("Circle Line from Edgware Road to Moorgate",
                ("Eastbound Train",),
                ("Hammersmith [0-9]{4}",)),
            # Handle ably when no line is specified but only one line serves the origin
            ('Arsenal',
                ('Cockfosters', 'Heathrow'),
                (str(WhensMyTransportException('no_line_specified', 'Arsenal')),)),
            # Handle ably when no line is specified but only one line serves both origin and destination
            ("Earl's Court to Plaistow",
                ('Upminster',),
                (str(WhensMyTransportException('no_line_specified_to', "Earl's Court", "Plaistow")),)),
            # Handle ably when no line is specified, there exists more than one line to get there, but we pick fastest
            ("Stockwell to Euston",
                ('Walthamstow Ctrl',),
                (str(WhensMyTransportException('no_line_specified_to', "Stockwell", "Euston")),)),            
        )

    def _test_correct_successes(self, tweet, _routes_specified, expected_origin, destination_to_avoid=''):
        """
        Generic test to confirm Tube Tweet is being processed correctly
        """
        print tweet.text
        t1 = time.time()
        results = self.bot.process_tweet(tweet)
        self.assertTrue(results)
        t2 = time.time()
        self.assertTrue(results)
        for result in results:
            print result
            self.assertTrue(result)
            self.assertNotEqual(result, result.upper())
            self.assertRegexpMatches(result, r"%s to .* [0-9]{4}" % expected_origin)
            if destination_to_avoid:
                self.assertNotRegexpMatches(result, destination_to_avoid)
        print 'Processing of Tweet took %0.3f ms\r\n' % ((t2 - t1) * 1000.0,)

    #
    # Core functionality tests
    #

    def test_location(self):
        """
        Unit tests for WMTLocation object and the Tube database
        """
        # Test station-finding works
        self.assertEqual(self.bot.geodata.find_closest((51.529444, -0.126944), {}).code, "KXX")
        self.assertEqual(self.bot.geodata.find_closest((51.529444, -0.126944), {'line': 'M'}).code, "KXX")
        self.assertEqual(self.bot.geodata.find_fuzzy_match("Kings Cross", {}).code, "KXX")
        self.assertEqual(self.bot.geodata.find_fuzzy_match("Kings Cross", {'line': 'M'}).code, "KXX")

        # Test route-tracing works as expected
        stockwell = self.bot.geodata.find_fuzzy_match("Stockwell", {})
        bank = self.bot.geodata.find_fuzzy_match("Bank", {})
        euston = self.bot.geodata.find_fuzzy_match("Euston", {})
        self.assertEqual(sorted(self.bot.geodata.get_lines_serving(stockwell)), ['N', 'V'])
        self.assertEqual(sorted(self.bot.geodata.get_lines_serving(bank)), ['C', 'N', 'W'])
        self.assertEqual(self.bot.geodata.length_of_route(stockwell, euston), 18)
        self.assertEqual(self.bot.geodata.length_of_route(stockwell, euston, 'N'), 20)
        self.assertIn(('Oxford Circus', '', 'Victoria'), self.bot.geodata.describe_route(stockwell, euston))
        self.assertIn(('Charing Cross', '', 'Northern'), self.bot.geodata.describe_route(stockwell, euston, "N"))
        self.assertIn(('Bank', '', 'Northern'), self.bot.geodata.describe_route(stockwell, euston, "N", bank))

        # Test route-testing works as expected
        west_ruislip = self.bot.geodata.find_fuzzy_match("West Ruislip", {})
        hainault = self.bot.geodata.find_fuzzy_match("Hainault", {})
        roding_valley = self.bot.geodata.find_fuzzy_match("Roding Valley", {})
        wanstead = self.bot.geodata.find_fuzzy_match("Wanstead", {})
        snaresbrook = self.bot.geodata.find_fuzzy_match("Snaresbrook", {})
        heathrow123 = self.bot.geodata.find_fuzzy_match("Heathrow Terminals 1, 2, 3", {})
        heathrow4 = self.bot.geodata.find_fuzzy_match("Heathrow Terminal 4", {})
        self.assertTrue(self.bot.geodata.direct_route_exists(west_ruislip, west_ruislip, "C"))
        self.assertTrue(self.bot.geodata.direct_route_exists(west_ruislip, hainault, "C"))
        self.assertTrue(self.bot.geodata.direct_route_exists(west_ruislip, roding_valley, "C", via=hainault))
        self.assertTrue(self.bot.geodata.direct_route_exists(west_ruislip, roding_valley, "C", via=hainault, must_stop_at=wanstead))
        self.assertFalse(self.bot.geodata.direct_route_exists(snaresbrook, wanstead, "C"))
        self.assertFalse(self.bot.geodata.direct_route_exists(heathrow123, heathrow4, "P"))
        self.assertFalse(self.bot.geodata.direct_route_exists(snaresbrook, heathrow123, "All"))
        self.assertFalse(self.bot.geodata.direct_route_exists(snaresbrook, heathrow123, "C"))

        # Test direction-finding works as expected
        morden = self.bot.geodata.find_fuzzy_match("Morden", {})
        high_barnet = self.bot.geodata.find_fuzzy_match("High Barnet", {})
        self.assertTrue(self.bot.geodata.is_correct_direction("Eastbound", west_ruislip, hainault, 'C'))
        self.assertTrue(self.bot.geodata.is_correct_direction("Westbound", hainault, west_ruislip, 'C'))
        self.assertTrue(self.bot.geodata.is_correct_direction("Northbound", morden, high_barnet, 'N'))
        self.assertTrue(self.bot.geodata.is_correct_direction("Southbound", hainault, wanstead, 'C'))
        self.assertFalse(self.bot.geodata.is_correct_direction("Southbound", snaresbrook, wanstead, 'C'))
        self.assertFalse(self.bot.geodata.is_correct_direction("Southbound", morden, high_barnet, 'N'))

        # DLR Location tests
        self.assertEqual(self.bot.geodata.find_closest((51.5124, -0.0397), {}).code, "lim")
        self.assertEqual(self.bot.geodata.find_closest((51.5124, -0.0397), {'line': 'DLR'}).code, "lim")
        self.assertEqual(self.bot.geodata.find_fuzzy_match("Limehouse", {}).code, "lim")
        self.assertEqual(self.bot.geodata.find_fuzzy_match("Limehouse", {'line': 'DLR'}).code, "lim")
        self.assertEqual(self.bot.geodata.find_fuzzy_match("Stratford Int", {}).code, "sti")
        self.assertEqual(self.bot.geodata.find_fuzzy_match("W'wich Arsenal", {}).code, "woa")

        stratford = self.bot.geodata.find_fuzzy_match("Stratford", {})
        beckton = self.bot.geodata.find_fuzzy_match("Beckton", {})
        poplar = self.bot.geodata.find_fuzzy_match("Poplar", {})
        self.assertIn(('West Ham', '', 'DLR'), self.bot.geodata.describe_route(stratford, beckton))
        self.assertIn(('Blackwall', '', 'DLR'), self.bot.geodata.describe_route(stratford, beckton, "DLR", poplar))

        limehouse = self.bot.geodata.find_fuzzy_match("Limehouse", {})
        all_saints = self.bot.geodata.find_fuzzy_match("All Saints", {})
        self.assertTrue(self.bot.geodata.direct_route_exists(limehouse, beckton, "DLR"))
        self.assertFalse(self.bot.geodata.direct_route_exists(limehouse, all_saints, "DLR"))
        self.assertTrue(self.bot.geodata.is_correct_direction("Eastbound", limehouse, beckton, "DLR"))
        self.assertFalse(self.bot.geodata.is_correct_direction("Eastbound", beckton, limehouse, "DLR"))

    def test_textparser(self):
        """
        Tests for the natural language parser
        """
        (line_name, origin, destination, direction) = ('Victoria', 'Sloane Square', 'Upminster', 'Eastbound')
        routes = [line_name]
        self.assertEqual(self.bot.parser.parse_message(""),                                                     (None, None, None, None))
        for route in (line_name, '%s Line' % line_name):
            self.assertEqual(self.bot.parser.parse_message("%s" % (route,)),                                    (routes, None, None, None))
            self.assertEqual(self.bot.parser.parse_message("%s %s" % (route, origin)),                          (routes, origin, None, None))
            self.assertEqual(self.bot.parser.parse_message("%s %s to %s" % (route, origin, destination)),       (routes, origin, destination, None))
            self.assertEqual(self.bot.parser.parse_message("%s from %s" % (route, origin)),                     (routes, origin, None, None))
            self.assertEqual(self.bot.parser.parse_message("%s from %s to %s" % (route, origin, destination)),  (routes, origin, destination, None))
            self.assertEqual(self.bot.parser.parse_message("%s to %s" % (route, destination)),                  (routes, None, destination, None))
            self.assertEqual(self.bot.parser.parse_message("%s to %s from %s" % (route, destination, origin)),  (routes, origin, destination, None))
            self.assertEqual(self.bot.parser.parse_message("%s %s" % (route, direction)),                       (routes, None, None, direction))
            self.assertEqual(self.bot.parser.parse_message("%s %s %s" % (route, origin, direction)),            (routes, origin, None, direction))
            self.assertEqual(self.bot.parser.parse_message("%s from %s %s" % (route, origin, direction)),       (routes, origin, None, direction))
            self.assertEqual(self.bot.parser.parse_message("%s %s %s" % (route, direction, origin)),            (routes, origin, None, direction))
            self.assertEqual(self.bot.parser.parse_message("%s %s from %s" % (route, direction, origin)),       (routes, origin, None, direction))

    #
    # Request-based tests
    #

    def test_bad_line_name(self):
        """
        Test to confirm bad line names are handled OK
        """
        message = 'Xrongwoihrwg line from Oxford Circus'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'nonexistent_line', 'Xrongwoihrwg')

    def test_bad_routing(self):
        """
        Test to confirm routes that are not possible on the DLR are correctly handled
        """
        message = 'DLR from Lewisham to Woolwich Arsenal'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'no_direct_route', 'Lewisham', 'Woolwich Arsenal', 'DLR')

    def test_missing_station_data(self):
        """
        Test to confirm certain stations which have no data are correctly reported
        """
        message = 'Metropolitan Line from Preston Road'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'rail_station_not_in_system', 'Preston Road')

    def test_station_line_mismatch(self):
        """
        Test to confirm stations on the wrong lines, or not on the system at all, are correctly error reported
        """
        message = 'District Line from Stratford'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'rail_station_name_not_found', 'Stratford', 'District Line')
        message = 'DLR from Ealing Broadway'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'rail_station_name_not_found', 'Ealing Broadway', 'DLR')
        message = 'Wxitythr Park'
        tweet = FakeTweet(self.at_reply + message)
        network_name = self.bot.default_requested_route  # Either 'Tube' or 'DLR'
        self._test_correct_exception_produced(tweet, 'rail_station_name_not_found', 'Wxitythr Park', network_name)

    @unittest.skipIf('--live-data' in sys.argv, "No trains unit test will fail on live data")
    def test_no_trains(self):
        """
        Test for when there are no trains at a station
        """
        # Handle when there are no trains from a station
        message = 'Waterloo & City Line from Bank'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'no_trains_shown', 'Waterloo & City Line', 'Bank')
        # Handle when there are no trains to a particular destination
        message = 'DLR from Lewisham to Poplar'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'no_trains_shown_to', 'DLR', 'Lewisham', 'Poplar')
        # Handle when there are no trains in a particular direction
        message = 'Central Line from Fairlop Westbound'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'no_trains_shown_in_direction', 'Westbound', 'Central Line', 'Fairlop')

    def test_no_line_specified(self):
        """
        Test for when no line is specified and it is impossible to deduce what the line is
        """
        # No direct line from Mansion House to Mornington Crescent
        message = 'Mansion House to Mornington Crescent'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'no_direct_route', 'Mansion House', 'Mornington Crescent', 'Tube')
        # Leicester Square has two lines, could be either
        message = 'Leicester Square'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'no_line_specified', 'Leicester Square')

    def test_known_problems(self):
        """
        Test known problematic inputs
        """
        # TODO Ideally, this function should be blank
        # District line from Victoria, or District & Victoria lines?
        message = 'District Victoria'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'nonexistent_line', message)
        # Not sure if "Waterloo to Bank" or "Waterloo & City Line to Bank"
        message = 'Waterloo to Bank'
        tweet = FakeTweet(self.at_reply + message)
        self._test_correct_exception_produced(tweet, 'no_geotag', message)

    @unittest.skipIf('--live-data' in sys.argv, "Expected responses to messages not replicable with live data")
    def test_standard_messages(self):
        """
        Generic test for standard-issue messages
        """
        #pylint: disable=W0612
        for (line, origin_name, lat, lon, destination_name, direction, expected_origin, destination_to_avoid) in self.standard_test_data:

            # C-string format helper
            test_variables = dict([(name, eval(name)) for name in ('line', 'origin_name', 'destination_name', 'line', 'direction')])

            # 2 types of origin (geotag, name) and 3 types of destination (none, name)
            from_fragments = [value % test_variables for value in ("", " %(origin_name)s", " from %(origin_name)s")]
            to_fragments = [value % test_variables for value in ("", " to %(destination_name)s", " %(direction)s")]
            # DLR allows blank Tweets as standard
            if self.bot.username == 'whensmydlr' and line == 'DLR':
                line_fragments = [value % test_variables for value in ("%(line)s", "")]
            elif line != 'DLR':
                line_fragments = [value % test_variables for value in ("%(line)s", ("%(line)s Line"))]
            else:
                line_fragments = [value % test_variables for value in ("%(line)s",)]

            for from_fragment in from_fragments:
                for to_fragment in to_fragments:
                    for line_fragment in line_fragments:
                        # There are some cases which cannot be used, e.g. "Victoria Victoria"
                        if line_fragment == from_fragment[1:]:
                            continue
                        messages = [(self.at_reply + line_fragment + from_fragment + to_fragment)]
                        # If we have a from in this, we can also put to first. from second
                        if from_fragment.startswith(" from"):
                            messages.append((self.at_reply + line_fragment + to_fragment + from_fragment))
                        for message in messages:
                            if not from_fragment:
                                tweet = FakeTweet(message, (lat, lon))
                            else:
                                tweet = FakeTweet(message)
                            self._test_correct_successes(tweet, line, expected_origin, to_fragment and destination_to_avoid)