| Index: appengine/monorail/framework/test/sorting_test.py
|
| diff --git a/appengine/monorail/framework/test/sorting_test.py b/appengine/monorail/framework/test/sorting_test.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2fe1176e6179edba4e07338604b54103c7ac4ef1
|
| --- /dev/null
|
| +++ b/appengine/monorail/framework/test/sorting_test.py
|
| @@ -0,0 +1,220 @@
|
| +# Copyright 2016 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is govered by a BSD-style
|
| +# license that can be found in the LICENSE file or at
|
| +# https://developers.google.com/open-source/licenses/bsd
|
| +
|
| +"""Unit tests for sorting.py functions."""
|
| +
|
| +import unittest
|
| +
|
| +import mox
|
| +
|
| +from framework import sorting
|
| +from proto import tracker_pb2
|
| +from testing import fake
|
| +from testing import testing_helpers
|
| +from tracker import tracker_bizobj
|
| +
|
| +
|
| +def MakeDescending(accessor):
|
| + return sorting._MaybeMakeDescending(accessor, True)
|
| +
|
| +
|
| +class SortingTest(unittest.TestCase):
|
| +
|
| + def setUp(self):
|
| + self.mox = mox.Mox()
|
| + self.default_cols = 'a b c'
|
| + self.builtin_cols = 'a b x y z'
|
| + self.config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
|
| + self.config.component_defs.append(tracker_bizobj.MakeComponentDef(
|
| + 11, 789, 'Database', 'doc', False, [], [], 0, 0))
|
| + self.config.component_defs.append(tracker_bizobj.MakeComponentDef(
|
| + 22, 789, 'User Interface', 'doc', True, [], [], 0, 0))
|
| + self.config.component_defs.append(tracker_bizobj.MakeComponentDef(
|
| + 33, 789, 'Installer', 'doc', False, [], [], 0, 0))
|
| +
|
| + def tearDown(self):
|
| + self.mox.UnsetStubs()
|
| + self.mox.ResetAll()
|
| +
|
| + def testMakeSingleSortKeyAccessor_Status(self):
|
| + """Sorting by status should create an accessor for that column."""
|
| + self.mox.StubOutWithMock(sorting, '_IndexOrLexical')
|
| + status_names = [wks.status for wks in self.config.well_known_statuses]
|
| + sorting._IndexOrLexical(status_names, 'status accessor')
|
| + self.mox.ReplayAll()
|
| +
|
| + sorting._MakeSingleSortKeyAccessor(
|
| + 'status', self.config, {'status': 'status accessor'}, [], {}, [])
|
| + self.mox.VerifyAll()
|
| +
|
| + def testMakeSingleSortKeyAccessor_Component(self):
|
| + """Sorting by component should create an accessor for that column."""
|
| + self.mox.StubOutWithMock(sorting, '_IndexListAccessor')
|
| + component_ids = [11, 33, 22]
|
| + sorting._IndexListAccessor(component_ids, 'component accessor')
|
| + self.mox.ReplayAll()
|
| +
|
| + sorting._MakeSingleSortKeyAccessor(
|
| + 'component', self.config, {'component': 'component accessor'}, [], {}, [])
|
| + self.mox.VerifyAll()
|
| +
|
| + def testMakeSingleSortKeyAccessor_OtherBuiltInColunms(self):
|
| + """Sorting a built-in column should create an accessor for that column."""
|
| + accessor = sorting._MakeSingleSortKeyAccessor(
|
| + 'buildincol', self.config, {'buildincol': 'accessor'}, [], {}, [])
|
| + self.assertEqual('accessor', accessor)
|
| +
|
| + def testMakeSingleSortKeyAccessor_OtherBuiltInUserColunms(self):
|
| + """Sorting a built-in user column should create a user accessor."""
|
| + self.mox.StubOutWithMock(sorting, '_UserEditNameAccessor')
|
| + users_by_id = {111L: 'fake user'}
|
| + sorting._UserEditNameAccessor(users_by_id, 'owner accessor')
|
| + self.mox.ReplayAll()
|
| +
|
| + sorting._MakeSingleSortKeyAccessor(
|
| + 'owner', self.config, {'owner': 'owner accessor'},
|
| + ['owner'], users_by_id, [])
|
| + self.mox.VerifyAll()
|
| +
|
| + def testIndexOrLexical(self):
|
| + well_known_values = ['x-a', 'x-b', 'x-c', 'x-d']
|
| + art = 'this is a fake artifact'
|
| +
|
| + # Case 1: accessor generates no values.
|
| + base_accessor = lambda art: None
|
| + accessor = sorting._IndexOrLexical(well_known_values, base_accessor)
|
| + self.assertEqual(sorting.MAX_STRING, accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual(sorting.DescendingValue(sorting.MAX_STRING),
|
| + neg_accessor(art))
|
| +
|
| + # Case 2: accessor generates a value, but it is an empty value.
|
| + base_accessor = lambda art: ''
|
| + accessor = sorting._IndexOrLexical(well_known_values, base_accessor)
|
| + self.assertEqual(sorting.MAX_STRING, accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual(sorting.DescendingValue(sorting.MAX_STRING),
|
| + neg_accessor(art))
|
| +
|
| + # Case 3: A single well-known value
|
| + base_accessor = lambda art: 'x-c'
|
| + accessor = sorting._IndexOrLexical(well_known_values, base_accessor)
|
| + self.assertEqual(2, accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual(-2, neg_accessor(art))
|
| +
|
| + # Case 4: A single odd-ball value
|
| + base_accessor = lambda art: 'x-zzz'
|
| + accessor = sorting._IndexOrLexical(well_known_values, base_accessor)
|
| + self.assertEqual('x-zzz', accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual(
|
| + sorting.DescendingValue('x-zzz'), neg_accessor(art))
|
| +
|
| + def testIndexListAccessor_SomeWellKnownValues(self):
|
| + """Values sort according to their position in the well-known list."""
|
| + well_known_values = [11, 33, 22] # These represent component IDs.
|
| + art = fake.MakeTestIssue(789, 1, 'sum 1', 'New', 111L)
|
| + base_accessor = lambda issue: issue.component_ids
|
| + accessor = sorting._IndexListAccessor(well_known_values, base_accessor)
|
| +
|
| + # Case 1: accessor generates no values.
|
| + self.assertEqual(sorting.MAX_STRING, accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual(sorting.MAX_STRING, neg_accessor(art))
|
| +
|
| + # Case 2: A single well-known value
|
| + art.component_ids = [33]
|
| + self.assertEqual([1], accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual([-1], neg_accessor(art))
|
| +
|
| + # Case 3: Multiple well-known and odd-ball values
|
| + art.component_ids = [33, 11, 99]
|
| + self.assertEqual([0, 1, sorting.MAX_STRING], accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual([sorting.MAX_STRING, -1, 0],
|
| + neg_accessor(art))
|
| +
|
| + def testIndexListAccessor_NoWellKnownValues(self):
|
| + """When there are no well-known values, all values sort last."""
|
| + well_known_values = [] # Nothing pre-defined, so everything is oddball
|
| + art = fake.MakeTestIssue(789, 1, 'sum 1', 'New', 111L)
|
| + base_accessor = lambda issue: issue.component_ids
|
| + accessor = sorting._IndexListAccessor(well_known_values, base_accessor)
|
| +
|
| + # Case 1: accessor generates no values.
|
| + self.assertEqual(sorting.MAX_STRING, accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual(sorting.MAX_STRING, neg_accessor(art))
|
| +
|
| + # Case 2: A single oddball value
|
| + art.component_ids = [33]
|
| + self.assertEqual([sorting.MAX_STRING], accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual([sorting.MAX_STRING], neg_accessor(art))
|
| +
|
| + # Case 3: Multiple odd-ball values
|
| + art.component_ids = [33, 11, 99]
|
| + self.assertEqual(
|
| + [sorting.MAX_STRING, sorting.MAX_STRING, sorting.MAX_STRING],
|
| + accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual(
|
| + [sorting.MAX_STRING, sorting.MAX_STRING, sorting.MAX_STRING],
|
| + neg_accessor(art))
|
| +
|
| + def testIndexOrLexicalList(self):
|
| + well_known_values = ['Pri-High', 'Pri-Med', 'Pri-Low']
|
| + art = fake.MakeTestIssue(789, 1, 'sum 1', 'New', 111L, merged_into=200001)
|
| +
|
| + # Case 1: accessor generates no values.
|
| + accessor = sorting._IndexOrLexicalList(well_known_values, [], 'pri', {})
|
| + self.assertEqual(sorting.MAX_STRING, accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual(sorting.MAX_STRING, neg_accessor(art))
|
| +
|
| + # Case 2: A single well-known value
|
| + art.labels = ['Pri-Med']
|
| + accessor = sorting._IndexOrLexicalList(well_known_values, [], 'pri', {})
|
| + self.assertEqual([1], accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual([-1], neg_accessor(art))
|
| +
|
| + # Case 3: Multiple well-known and odd-ball values
|
| + art.labels = ['Pri-zzz', 'Pri-Med', 'yyy', 'Pri-High']
|
| + accessor = sorting._IndexOrLexicalList(well_known_values, [], 'pri', {})
|
| + self.assertEqual([0, 1, 'zzz'], accessor(art))
|
| + neg_accessor = MakeDescending(accessor)
|
| + self.assertEqual([sorting.DescendingValue('zzz'), -1, 0],
|
| + neg_accessor(art))
|
| +
|
| + def testComputeSortDirectives(self):
|
| + config = tracker_pb2.ProjectIssueConfig()
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + path='/p/proj/issues/detail?id=123')
|
| + self.assertEquals(['project', 'id'],
|
| + sorting.ComputeSortDirectives(mr, config))
|
| +
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + path='/p/proj/issues/detail?id=123&sort=a b C')
|
| + self.assertEquals(['a', 'b', 'c', 'project', 'id'],
|
| + sorting.ComputeSortDirectives(mr, config))
|
| +
|
| + config.default_sort_spec = 'id -reporter Owner'
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + path='/p/proj/issues/detail?id=123')
|
| + self.assertEquals(['id', '-reporter', 'owner', 'project'],
|
| + sorting.ComputeSortDirectives(mr, config))
|
| +
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + path='/p/proj/issues/detail?id=123&sort=A -b c -owner')
|
| + self.assertEquals(
|
| + ['a', '-b', 'c', '-owner', 'id', '-reporter', 'project'],
|
| + sorting.ComputeSortDirectives(mr, config))
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + unittest.main()
|
|
|