| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import json | 6 import json |
| 7 import os | 7 import os |
| 8 import sys | 8 import sys |
| 9 import unittest | 9 import unittest |
| 10 | 10 |
| 11 from api_data_source import (_JSCModel, | 11 from api_data_source import (_JSCModel, |
| 12 _FormatValue, | 12 _FormatValue, |
| 13 _GetEventByNameFromEvents) | 13 _GetEventByNameFromEvents) |
| 14 from api_schema_graph import APISchemaGraph | 14 from api_schema_graph import APISchemaGraph |
| 15 from availability_finder import AvailabilityInfo | 15 from availability_finder import AvailabilityFinder, AvailabilityInfo |
| 16 from branch_utility import ChannelInfo | 16 from branch_utility import BranchUtility, ChannelInfo |
| 17 from compiled_file_system import CompiledFileSystem |
| 17 from extensions_paths import CHROME_EXTENSIONS | 18 from extensions_paths import CHROME_EXTENSIONS |
| 18 from fake_host_file_system_provider import FakeHostFileSystemProvider | 19 from fake_host_file_system_provider import FakeHostFileSystemProvider |
| 20 from fake_url_fetcher import FakeUrlFetcher |
| 19 from features_bundle import FeaturesBundle | 21 from features_bundle import FeaturesBundle |
| 20 from file_system import FileNotFoundError | 22 from file_system import FileNotFoundError |
| 21 from future import Future | 23 from future import Future |
| 24 from host_file_system_iterator import HostFileSystemIterator |
| 22 from object_store_creator import ObjectStoreCreator | 25 from object_store_creator import ObjectStoreCreator |
| 23 from server_instance import ServerInstance | 26 from server_instance import ServerInstance |
| 27 from test_data.api_data_source.canned_trunk_fs import CANNED_TRUNK_FS_DATA |
| 24 from test_data.canned_data import (CANNED_API_FILE_SYSTEM_DATA, CANNED_BRANCHES) | 28 from test_data.canned_data import (CANNED_API_FILE_SYSTEM_DATA, CANNED_BRANCHES) |
| 25 from test_data.api_data_source.canned_trunk_fs import CANNED_TRUNK_FS_DATA | 29 from test_data.object_level_availability.tabs import TABS_SCHEMA_BRANCHES |
| 26 from test_file_system import TestFileSystem | 30 from test_file_system import TestFileSystem |
| 27 from test_util import Server2Path | 31 from test_util import Server2Path |
| 28 from third_party.json_schema_compiler.memoize import memoize | 32 from third_party.json_schema_compiler.memoize import memoize |
| 29 | 33 |
| 30 | 34 |
| 31 def _MakeLink(href, text): | |
| 32 return '<a href="%s">%s</a>' % (href, text) | |
| 33 | |
| 34 | |
| 35 def _GetType(dict_, name): | |
| 36 for type_ in dict_['types']: | |
| 37 if type_['name'] == name: | |
| 38 return type_ | |
| 39 | |
| 40 | |
| 41 class _FakeTemplateCache(object): | 35 class _FakeTemplateCache(object): |
| 42 | 36 |
| 43 def GetFromFile(self, key): | 37 def GetFromFile(self, key): |
| 44 return Future(value='handlebar %s' % key) | 38 return Future(value='handlebar %s' % key) |
| 45 | 39 |
| 46 | 40 |
| 41 class _FakeAvailabilityFinder(object): |
| 42 def __init__(self, fake_availability): |
| 43 self._fake_availability = fake_availability |
| 44 |
| 45 def GetAPIAvailability(self, api_name): |
| 46 return self._fake_availability |
| 47 |
| 48 def GetAPINodeAvailability(self, api_name): |
| 49 '''The tests that use this fake class don't |
| 50 use the node availability, so just return a |
| 51 dummy graph. |
| 52 ''' |
| 53 return APISchemaGraph(_graph={'dummy': 'graph'}) |
| 54 |
| 55 |
| 47 class _FakeFeaturesBundle(object): | 56 class _FakeFeaturesBundle(object): |
| 48 def GetAPIFeatures(self): | 57 def GetAPIFeatures(self): |
| 49 return Future(value={ | 58 return Future(value={ |
| 50 'bluetooth': {'value': True}, | 59 'bluetooth': {'value': True}, |
| 51 'contextMenus': {'value': True}, | 60 'contextMenus': {'value': True}, |
| 52 'jsonStableAPI': {'value': True}, | 61 'jsonStableAPI': {'value': True}, |
| 53 'idle': {'value': True}, | 62 'idle': {'value': True}, |
| 54 'input.ime': {'value': True}, | 63 'input.ime': {'value': True}, |
| 55 'tabs': {'value': True} | 64 'tabs': {'value': True} |
| 56 }) | 65 }) |
| 57 | 66 |
| 58 | 67 |
| 59 class _FakeAvailabilityFinder(object): | 68 class _FakeAvailabilityFinder(object): |
| 60 def __init__(self, fake_availability): | 69 def __init__(self, fake_availability): |
| 61 self._fake_availability = fake_availability | 70 self._fake_availability = fake_availability |
| 62 | 71 |
| 63 def GetAPIAvailability(self, api_name): | 72 def GetAPIAvailability(self, api_name): |
| 64 return self._fake_availability | 73 return self._fake_availability |
| 65 | 74 |
| 66 def GetAPINodeAvailability(self, api_name): | 75 def GetAPINodeAvailability(self, api_name): |
| 67 '''The tests that use this fake class don't | 76 '''The tests that use this fake class don't |
| 68 use the node availability, so just return a | 77 use the node availability, so just return a |
| 69 dummy graph. | 78 dummy graph. |
| 70 ''' | 79 ''' |
| 71 return APISchemaGraph(_graph={'dummy': 'graph'}) | 80 return APISchemaGraph(_graph={'dummy': 'graph'}) |
| 72 | 81 |
| 73 | 82 |
| 74 class APIDataSourceTest(unittest.TestCase): | 83 class APIDataSourceTest(unittest.TestCase): |
| 75 | |
| 76 def setUp(self): | 84 def setUp(self): |
| 77 self._base_path = Server2Path('test_data', 'test_json') | 85 self._base_path = Server2Path('test_data', 'test_json') |
| 78 | 86 |
| 79 server_instance = ServerInstance.ForTest( | 87 server_instance = ServerInstance.ForTest( |
| 80 TestFileSystem(CANNED_TRUNK_FS_DATA, relative_to=CHROME_EXTENSIONS)) | 88 TestFileSystem(CANNED_TRUNK_FS_DATA, relative_to=CHROME_EXTENSIONS)) |
| 81 file_system = server_instance.host_file_system_provider.GetTrunk() | 89 file_system = server_instance.host_file_system_provider.GetTrunk() |
| 82 self._json_cache = server_instance.compiled_fs_factory.ForJson(file_system) | 90 self._json_cache = server_instance.compiled_fs_factory.ForJson(file_system) |
| 83 self._features_bundle = FeaturesBundle(file_system, | 91 self._features_bundle = FeaturesBundle(file_system, |
| 84 server_instance.compiled_fs_factory, | 92 server_instance.compiled_fs_factory, |
| 85 server_instance.object_store_creator, | 93 server_instance.object_store_creator, |
| 86 'extensions') | 94 'extensions') |
| 87 self._api_models = server_instance.platform_bundle.GetAPIModels( | 95 self._api_models = server_instance.platform_bundle.GetAPIModels( |
| 88 'extensions') | 96 'extensions') |
| 89 self._fake_availability = AvailabilityInfo(ChannelInfo('stable', '396', 5)) | 97 self._fake_availability = AvailabilityInfo(ChannelInfo('stable', '396', 5)) |
| 90 | 98 |
| 91 # Used for testGetAPIAvailability() so that valid-ish data is processed. | |
| 92 server_instance = ServerInstance.ForTest( | |
| 93 file_system_provider=FakeHostFileSystemProvider( | |
| 94 CANNED_API_FILE_SYSTEM_DATA)) | |
| 95 self._avail_api_models = server_instance.platform_bundle.GetAPIModels( | |
| 96 'extensions') | |
| 97 self._avail_json_cache = server_instance.compiled_fs_factory.ForJson( | |
| 98 server_instance.host_file_system_provider.GetTrunk()) | |
| 99 self._avail_finder = server_instance.platform_bundle.GetAvailabilityFinder( | |
| 100 'extensions') | |
| 101 | |
| 102 def _ReadLocalFile(self, filename): | 99 def _ReadLocalFile(self, filename): |
| 103 with open(os.path.join(self._base_path, filename), 'r') as f: | 100 with open(os.path.join(self._base_path, filename), 'r') as f: |
| 104 return f.read() | 101 return f.read() |
| 105 | 102 |
| 106 def _LoadJSON(self, filename): | 103 def _LoadJSON(self, filename): |
| 107 return json.loads(self._ReadLocalFile(filename)) | 104 return json.loads(self._ReadLocalFile(filename)) |
| 108 | 105 |
| 106 def _FakeLoadAddRulesSchema(self): |
| 107 events = self._LoadJSON('add_rules_def_test.json') |
| 108 return Future(value=_GetEventByNameFromEvents(events)) |
| 109 |
| 110 def testFormatValue(self): |
| 111 self.assertEquals('1,234,567', _FormatValue(1234567)) |
| 112 self.assertEquals('67', _FormatValue(67)) |
| 113 self.assertEquals('234,567', _FormatValue(234567)) |
| 114 |
| 115 def testGetEventByNameFromEvents(self): |
| 116 events = {} |
| 117 # Missing 'types' completely. |
| 118 self.assertRaises(AssertionError, _GetEventByNameFromEvents, events) |
| 119 |
| 120 events['types'] = [] |
| 121 # No type 'Event' defined. |
| 122 self.assertRaises(AssertionError, _GetEventByNameFromEvents, events) |
| 123 |
| 124 events['types'].append({ 'name': 'Event', |
| 125 'functions': []}) |
| 126 add_rules = { "name": "addRules" } |
| 127 events['types'][0]['functions'].append(add_rules) |
| 128 self.assertEqual(add_rules, |
| 129 _GetEventByNameFromEvents(events)['addRules']) |
| 130 |
| 131 events['types'][0]['functions'].append(add_rules) |
| 132 # Duplicates are an error. |
| 133 self.assertRaises(AssertionError, _GetEventByNameFromEvents, events) |
| 134 |
| 109 def testCreateId(self): | 135 def testCreateId(self): |
| 110 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) | 136 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) |
| 111 dict_ = _JSCModel(self._api_models.GetModel('tester').Get(), | 137 dict_ = _JSCModel(self._api_models.GetModel('tester').Get(), |
| 112 fake_avail_finder, | 138 fake_avail_finder, |
| 113 self._json_cache, | 139 self._json_cache, |
| 114 _FakeTemplateCache(), | 140 _FakeTemplateCache(), |
| 115 self._features_bundle, | 141 self._features_bundle, |
| 116 None).ToDict() | 142 None).ToDict() |
| 117 self.assertEquals('type-TypeA', dict_['types'][0]['id']) | 143 self.assertEquals('type-TypeA', dict_['types'][0]['id']) |
| 118 self.assertEquals('property-TypeA-b', | 144 self.assertEquals('property-TypeA-b', |
| 119 dict_['types'][0]['properties'][0]['id']) | 145 dict_['types'][0]['properties'][0]['id']) |
| 120 self.assertEquals('method-get', dict_['functions'][0]['id']) | 146 self.assertEquals('method-get', dict_['functions'][0]['id']) |
| 121 self.assertEquals('event-EventA', dict_['events'][0]['id']) | 147 self.assertEquals('event-EventA', dict_['events'][0]['id']) |
| 122 | 148 |
| 123 # TODO(kalman): re-enable this when we have a rebase option. | 149 # TODO(kalman): re-enable this when we have a rebase option. |
| 124 def DISABLED_testToDict(self): | 150 def DISABLED_testToDict(self): |
| 125 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) | 151 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) |
| 126 expected_json = self._LoadJSON('expected_tester.json') | 152 expected_json = self._LoadJSON('expected_tester.json') |
| 127 dict_ = _JSCModel(self._api_models.GetModel('tester').Get(), | 153 dict_ = _JSCModel(self._api_models.GetModel('tester').Get(), |
| 128 fake_avail_finder, | 154 fake_avail_finder, |
| 129 self._json_cache, | 155 self._json_cache, |
| 130 _FakeTemplateCache(), | 156 _FakeTemplateCache(), |
| 131 self._features_bundle, | 157 self._features_bundle, |
| 132 None).ToDict() | 158 None).ToDict() |
| 133 self.assertEquals(expected_json, dict_) | 159 self.assertEquals(expected_json, dict_) |
| 134 | 160 |
| 135 def testFormatValue(self): | 161 def testAddRules(self): |
| 136 self.assertEquals('1,234,567', _FormatValue(1234567)) | 162 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) |
| 137 self.assertEquals('67', _FormatValue(67)) | 163 dict_ = _JSCModel(self._api_models.GetModel('add_rules_tester').Get(), |
| 138 self.assertEquals('234,567', _FormatValue(234567)) | 164 fake_avail_finder, |
| 165 #self._fake_availability, |
| 166 self._json_cache, |
| 167 _FakeTemplateCache(), |
| 168 self._features_bundle, |
| 169 self._FakeLoadAddRulesSchema()).ToDict() |
| 139 | 170 |
| 140 def testGetAPIAvailability(self): | 171 # Check that the first event has the addRulesFunction defined. |
| 141 api_availabilities = { | 172 self.assertEquals('add_rules_tester', dict_['name']) |
| 142 'bluetooth': 28, | 173 self.assertEquals('rules', dict_['events'][0]['name']) |
| 143 'contextMenus': 'trunk', | 174 self.assertEquals('notable_name_to_check_for', |
| 144 'jsonStableAPI': 20, | 175 dict_['events'][0]['byName']['addRules'][ |
| 145 'idle': 5, | 176 'parameters'][0]['name']) |
| 146 'input.ime': 18, | 177 |
| 147 'signedInDevices': 'trunk', | 178 # Check that the second event has addListener defined. |
| 148 'systemInfo.stuff': 28, | 179 self.assertEquals('noRules', dict_['events'][1]['name']) |
| 149 'tabs': 18 | 180 self.assertEquals('add_rules_tester', dict_['name']) |
| 150 } | 181 self.assertEquals('noRules', dict_['events'][1]['name']) |
| 151 for api_name, availability in api_availabilities.iteritems(): | 182 self.assertEquals('callback', |
| 152 model_dict = _JSCModel( | 183 dict_['events'][0]['byName']['addListener'][ |
| 153 self._avail_api_models.GetModel(api_name).Get(), | 184 'parameters'][0]['name']) |
| 154 self._avail_finder, | |
| 155 self._avail_json_cache, | |
| 156 _FakeTemplateCache(), | |
| 157 _FakeFeaturesBundle(), | |
| 158 None).ToDict() | |
| 159 self.assertEquals(availability, | |
| 160 model_dict['introList'][1]['content'][0]['version']) | |
| 161 | 185 |
| 162 def testGetIntroList(self): | 186 def testGetIntroList(self): |
| 163 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) | 187 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) |
| 164 model = _JSCModel(self._api_models.GetModel('tester').Get(), | 188 model = _JSCModel(self._api_models.GetModel('tester').Get(), |
| 165 fake_avail_finder, | 189 fake_avail_finder, |
| 166 self._json_cache, | 190 self._json_cache, |
| 167 _FakeTemplateCache(), | 191 _FakeTemplateCache(), |
| 168 self._features_bundle, | 192 self._features_bundle, |
| 169 None) | 193 None) |
| 170 expected_list = [ | 194 expected_list = [ |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 'content': [ | 245 'content': [ |
| 222 { 'partial': 'handlebar chrome/common/extensions/docs/' + | 246 { 'partial': 'handlebar chrome/common/extensions/docs/' + |
| 223 'templates/private/intro_tables/beta_message.html', | 247 'templates/private/intro_tables/beta_message.html', |
| 224 'version': 27, | 248 'version': 27, |
| 225 'scheduled': 28 | 249 'scheduled': 28 |
| 226 } | 250 } |
| 227 ] | 251 ] |
| 228 } | 252 } |
| 229 self.assertEquals(model._GetIntroTableList(), expected_list) | 253 self.assertEquals(model._GetIntroTableList(), expected_list) |
| 230 | 254 |
| 231 def testGetEventByNameFromEvents(self): | |
| 232 events = {} | |
| 233 # Missing 'types' completely. | |
| 234 self.assertRaises(AssertionError, _GetEventByNameFromEvents, events) | |
| 235 | 255 |
| 236 events['types'] = [] | 256 class APIDataSourceWithoutNodeAvailabilityTest(unittest.TestCase): |
| 237 # No type 'Event' defined. | 257 def setUp(self): |
| 238 self.assertRaises(AssertionError, _GetEventByNameFromEvents, events) | 258 server_instance = ServerInstance.ForTest( |
| 259 file_system_provider=FakeHostFileSystemProvider( |
| 260 CANNED_API_FILE_SYSTEM_DATA)) |
| 261 self._api_models = server_instance.platform_bundle.GetAPIModels( |
| 262 'extensions') |
| 263 self._json_cache = server_instance.compiled_fs_factory.ForJson( |
| 264 server_instance.host_file_system_provider.GetTrunk()) |
| 265 self._avail_finder = server_instance.platform_bundle.GetAvailabilityFinder( |
| 266 'extensions') |
| 239 | 267 |
| 240 events['types'].append({ 'name': 'Event', | |
| 241 'functions': []}) | |
| 242 add_rules = { "name": "addRules" } | |
| 243 events['types'][0]['functions'].append(add_rules) | |
| 244 self.assertEqual(add_rules, | |
| 245 _GetEventByNameFromEvents(events)['addRules']) | |
| 246 | 268 |
| 247 events['types'][0]['functions'].append(add_rules) | 269 def testGetAPIAvailability(self): |
| 248 # Duplicates are an error. | 270 api_availabilities = { |
| 249 self.assertRaises(AssertionError, _GetEventByNameFromEvents, events) | 271 'bluetooth': 28, |
| 272 'contextMenus': 'trunk', |
| 273 'jsonStableAPI': 20, |
| 274 'idle': 5, |
| 275 'input.ime': 18, |
| 276 'tabs': 18 |
| 277 } |
| 278 for api_name, availability in api_availabilities.iteritems(): |
| 279 model_dict = _JSCModel( |
| 280 self._api_models.GetModel(api_name).Get(), |
| 281 self._avail_finder, |
| 282 self._json_cache, |
| 283 _FakeTemplateCache(), |
| 284 _FakeFeaturesBundle(), |
| 285 None).ToDict() |
| 286 self.assertEquals(availability, |
| 287 model_dict['introList'][1]['content'][0]['version']) |
| 250 | 288 |
| 251 def _FakeLoadAddRulesSchema(self): | |
| 252 events = self._LoadJSON('add_rules_def_test.json') | |
| 253 return Future(value=_GetEventByNameFromEvents(events)) | |
| 254 | 289 |
| 255 def testAddRules(self): | 290 class APIDataSourceWithNodeAvailabilityTest(unittest.TestCase): |
| 256 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) | 291 def setUp(self): |
| 257 dict_ = _JSCModel(self._api_models.GetModel('add_rules_tester').Get(), | 292 tabs_unmodified_versions = (16, 20, 23, 24) |
| 258 fake_avail_finder, | 293 self._branch_utility = BranchUtility( |
| 259 self._json_cache, | 294 os.path.join('branch_utility', 'first.json'), |
| 260 _FakeTemplateCache(), | 295 os.path.join('branch_utility', 'second.json'), |
| 261 self._features_bundle, | 296 FakeUrlFetcher(Server2Path('test_data')), |
| 262 self._FakeLoadAddRulesSchema()).ToDict() | 297 ObjectStoreCreator.ForTest()) |
| 298 self._node_fs_creator = FakeHostFileSystemProvider(TABS_SCHEMA_BRANCHES) |
| 299 self._node_fs_iterator = HostFileSystemIterator(self._node_fs_creator, |
| 300 self._branch_utility) |
| 301 test_object_store = ObjectStoreCreator.ForTest() |
| 302 self._avail_finder = AvailabilityFinder( |
| 303 self._branch_utility, |
| 304 CompiledFileSystem.Factory(test_object_store), |
| 305 self._node_fs_iterator, |
| 306 self._node_fs_creator.GetTrunk(), |
| 307 test_object_store, |
| 308 'extensions') |
| 263 | 309 |
| 264 # Check that the first event has the addRulesFunction defined. | 310 server_instance = ServerInstance.ForTest( |
| 265 self.assertEquals('add_rules_tester', dict_['name']) | 311 file_system_provider=FakeHostFileSystemProvider( |
| 266 self.assertEquals('rules', dict_['events'][0]['name']) | 312 TABS_SCHEMA_BRANCHES)) |
| 267 self.assertEquals('notable_name_to_check_for', | 313 self._api_models = server_instance.platform_bundle.GetAPIModels( |
| 268 dict_['events'][0]['byName']['addRules'][ | 314 'extensions') |
| 269 'parameters'][0]['name']) | 315 self._json_cache = server_instance.compiled_fs_factory.ForJson( |
| 316 server_instance.host_file_system_provider.GetTrunk()) |
| 270 | 317 |
| 271 # Check that the second event has addListener defined. | 318 # Imitate the actual SVN file system by incrementing the stats for paths |
| 272 self.assertEquals('noRules', dict_['events'][1]['name']) | 319 # where an API schema has changed. |
| 273 self.assertEquals('add_rules_tester', dict_['name']) | 320 last_stat = type('last_stat', (object,), {'val': 0}) |
| 274 self.assertEquals('noRules', dict_['events'][1]['name']) | 321 |
| 275 self.assertEquals('callback', | 322 def stat_paths(file_system, channel_info): |
| 276 dict_['events'][0]['byName']['addListener'][ | 323 if channel_info.version not in tabs_unmodified_versions: |
| 277 'parameters'][0]['name']) | 324 last_stat.val += 1 |
| 325 # HACK: |file_system| is a MockFileSystem backed by a TestFileSystem. |
| 326 # Increment the TestFileSystem stat count. |
| 327 file_system._file_system.IncrementStat(by=last_stat.val) |
| 328 # Continue looping. The iterator will stop after 'trunk' automatically. |
| 329 return True |
| 330 |
| 331 # Use the HostFileSystemIterator created above to change global stat values |
| 332 # for the TestFileSystems that it creates. |
| 333 self._node_fs_iterator.Ascending( |
| 334 # The earliest version represented with the tabs' test data is 13. |
| 335 self._branch_utility.GetStableChannelInfo(13), |
| 336 stat_paths) |
| 337 |
| 338 def testGetAPINodeAvailability(self): |
| 339 def assertEquals(node, actual): |
| 340 node_availabilities = { |
| 341 'tabs.get': None, |
| 342 'tabs.getCurrent': 19 |
| 343 } |
| 344 self.assertEquals(node_availabilities[node], actual) |
| 345 |
| 346 model_dict = _JSCModel( |
| 347 self._api_models.GetModel('tabs').Get(), |
| 348 self._avail_finder, |
| 349 self._json_cache, |
| 350 _FakeTemplateCache(), |
| 351 _FakeFeaturesBundle(), |
| 352 None).ToDict() |
| 353 |
| 354 # Test nodes that have the same availability as their parent. |
| 355 assertEquals('tabs.get', model_dict['functions'][1]['availability']) |
| 356 # Test nodes with varying availabilities. |
| 357 assertEquals('tabs.getCurrent', |
| 358 model_dict['functions'][0]['availability']['version']) |
| 359 |
| 278 | 360 |
| 279 if __name__ == '__main__': | 361 if __name__ == '__main__': |
| 280 unittest.main() | 362 unittest.main() |
| OLD | NEW |