| # Copyright 2016 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Module to get random numbers, strings, etc. |
| |
| The values returned by the various functions can be replaced in |
| templates to generate test cases. |
| """ |
| |
| import math |
| import random |
| import sys |
| import uuid |
| |
| # This script needs the utils.py and fuzzy_types.py modules in order |
| # to work. This files are copied by the setup.py script and not checked-in |
| # next to this code, so we need to disable the style warning. |
| # pylint: disable=F0401 |
| from resources import utils |
| from resources import fuzzy_types |
| |
| import gatt_aliases |
| import wbt_fakes |
| |
| |
| # Strings that are used to generate the beginning of a test. The replacement |
| # fields are replaced by Get*Base() functions below to generate valid test |
| # cases. |
| BASIC_BASE = \ |
| ' return setBluetoothFakeAdapter({fake_adapter_name})\n'\ |
| ' .then(() => {{\n' |
| |
| DEVICE_DISCOVERY_BASE = BASIC_BASE + \ |
| ' return requestDeviceWithKeyDown({{\n'\ |
| ' filters: [{{services: [{service_uuid}]}}]}});\n'\ |
| ' }})\n'\ |
| ' .then(device => {{\n' |
| |
| CONNECTABLE_BASE = DEVICE_DISCOVERY_BASE + \ |
| ' return device.gatt.connect();\n'\ |
| ' }})\n'\ |
| ' .then(gatt => {{\n' |
| |
| SERVICE_RETRIEVED_BASE = CONNECTABLE_BASE + \ |
| ' return gatt.getPrimaryService({service_uuid});\n'\ |
| ' }})\n'\ |
| ' .then(services => {{\n' |
| |
| SERVICES_RETRIEVED_BASE = CONNECTABLE_BASE + \ |
| ' return gatt.getPrimaryServices({optional_service_uuid});\n'\ |
| ' }})\n'\ |
| ' .then(services => {{\n' |
| |
| CHARACTERISTIC_RETRIEVED_BASE = \ |
| ' TRANSFORM_PICK_A_SERVICE;\n'\ |
| ' return service.getCharacteristic({characteristic_uuid});\n'\ |
| ' }})\n'\ |
| ' .then(characteristics => {{\n' |
| |
| CHARACTERISTICS_RETRIEVED_BASE = \ |
| ' TRANSFORM_PICK_A_SERVICE;\n'\ |
| ' return service.getCharacteristics({optional_characteristic_uuid});\n'\ |
| ' }})\n'\ |
| ' .then(characteristics => {{\n' |
| |
| |
| def _ToJsStr(s): |
| return u'\'{}\''.format(s) |
| |
| |
| def _get_random_number(): |
| return utils.UniformExpoInteger(0, sys.maxsize.bit_length() + 1) |
| |
| |
| def _GetFuzzedJsString(s): |
| """Returns a fuzzed string based on |s|. |
| |
| Args: |
| s: The base string to fuzz. |
| Returns: |
| A single line string surrounded by quotes. |
| """ |
| while True: |
| fuzzed_string = fuzzy_types.FuzzyString(s) |
| try: |
| fuzzed_string = fuzzed_string.decode('utf8') |
| except UnicodeDecodeError: |
| print 'Can\'t decode fuzzed string. Trying again.' |
| else: |
| # Escape 'escape' characters. |
| fuzzed_string = fuzzed_string.replace('\\', r'\\') |
| # Escape quote characters. |
| fuzzed_string = fuzzed_string.replace('\'', r'\'') |
| # Put everything in a single line. |
| fuzzed_string = '\\n'.join(fuzzed_string.split()) |
| return _ToJsStr(fuzzed_string) |
| |
| |
| def _get_array_of_random_ints(max_length, max_value): |
| """Returns an string with an array of random integer.""" |
| length = utils.UniformExpoInteger(0, math.log(max_length, 2)) |
| exp_max_value = math.log(max_value, 2) |
| return '[{}]'.format(', '.join( |
| str(utils.UniformExpoInteger(0, exp_max_value)) for _ in xrange(length)) |
| ) |
| |
| |
| def _get_typed_array(): |
| """Generates a TypedArray constructor. |
| |
| There are nine types of TypedArrays and TypedArray has four constructors. |
| |
| Types: |
| * Int8Array |
| * Int16Array |
| * Int32Array |
| * Uint8Array |
| * Uint16Array |
| * Uint32Array |
| * Uint8ClampedArray |
| * Float32Array |
| * Float64Array |
| |
| Constructors: |
| |
| * new TypedArray(length) |
| * new TypedArray(typedArray) |
| * new TypedArray(object) |
| * new TypedArray(buffer) |
| |
| Returns: |
| A string made up of a randomly chosen type and argument type from the |
| lists above. |
| """ |
| array_type = random.choice(['Int8Array', 'Int16Array', 'Int32Array', |
| 'Uint8Array', 'Uint16Array', 'Uint32Array', |
| 'Uint8ClampedArray', 'Float32Array', |
| 'Float64Array']) |
| |
| # Choose an argument type at random. |
| arguments = random.choice([ |
| # length e.g. 293 |
| # We choose 2**10 as the upper boundry because the max length allowed |
| # by WebBluetooth is 2**10. |
| lambda: utils.UniformExpoInteger(0, 10), |
| # typedArray e.g. new Uint8Array([1,2,3]) |
| _get_typed_array, |
| # object e.g. [1,2,3] |
| lambda: _get_array_of_random_ints(max_length=1000, max_value=2 ** 64), |
| # buffer e.g. new Uint8Array(10).buffer |
| lambda: _get_typed_array() + '.buffer', |
| ]) |
| |
| return 'new {array_type}({arguments})'.format(array_type=array_type, |
| arguments=arguments()) |
| |
| |
| def GetAdvertisedServiceUUIDFromFakes(): |
| """Returns a random service string from the list of fake services.""" |
| return _ToJsStr(random.choice(wbt_fakes.ADVERTISED_SERVICES)) |
| |
| |
| def get_service_uuid_from_fakes(): |
| """Returns a random service string from a list of fake services.""" |
| return _ToJsStr(random.choice(wbt_fakes.SERVICES)) |
| |
| |
| def get_characteristic_uuid_from_fakes(): |
| """Returns a random characteristic string from a fake characteristics list.""" |
| return _ToJsStr(random.choice(wbt_fakes.CHARACTERISTICS)) |
| |
| |
| def GetValidServiceAlias(): |
| """Returns a valid service alias from the list of services aliases.""" |
| return _ToJsStr(random.choice(gatt_aliases.SERVICES)) |
| |
| |
| def get_valid_characteristic_alias(): |
| """Returns a valid service alias from the list of services aliases.""" |
| return _ToJsStr(random.choice(gatt_aliases.CHARACTERISTICS)) |
| |
| |
| def GetRandomUUID(): |
| """Returns a random UUID, a random number or a fuzzed uuid or alias.""" |
| choice = random.choice(['uuid', 'number', 'fuzzed string']) |
| if choice == 'uuid': |
| return _ToJsStr(uuid.uuid4()) |
| elif choice == 'number': |
| return _get_random_number() |
| elif choice == 'fuzzed string': |
| choice2 = random.choice(['uuid', 'alias']) |
| if choice2 == 'uuid': |
| random_uuid = str(uuid.uuid4()) |
| return _GetFuzzedJsString(random_uuid) |
| elif choice2 == 'alias': |
| alias = random.choice(gatt_aliases.SERVICES) |
| return _GetFuzzedJsString(alias) |
| |
| |
| def GetAdvertisedServiceUUID(): |
| """Generates a random Service UUID from a set of functions. |
| |
| See get_service_uuid_from_fakes(), GetValidServiceAlias() and GetRandomUUID() |
| for the different values this function can return. |
| |
| This function weights get_service_uuid_from_fakes() more heavily to increase the |
| probability of generating test pages that can interact with the fake |
| adapters. |
| |
| Returns: |
| A string or a number that can be used as a Service UUID by the Web |
| Bluetooth API. |
| """ |
| roll = random.random() |
| if roll < 0.8: |
| return GetAdvertisedServiceUUIDFromFakes() |
| elif roll < 0.9: |
| return GetValidServiceAlias() |
| else: |
| return GetRandomUUID() |
| |
| |
| def get_service_uuid(): |
| """Generates a random Service UUID from a set of functions. |
| |
| Similar to GetAdvertisedServiceUUID() but weights get_service_uuid_from_fakes() |
| more heavily to increase the probability of generating test pages that can |
| interact with the fake adapters. |
| |
| See get_service_uuid_from_fakes(), GetValidServiceAlias() and |
| GetRandomUUID() for the different values this function can return. |
| |
| Returns: |
| A string or a number that can be used as a Service UUID by the Web |
| Bluetooth API. |
| """ |
| roll = random.random() |
| if roll < 0.8: |
| return get_service_uuid_from_fakes() |
| elif roll < 0.9: |
| return GetValidServiceAlias() |
| else: |
| return GetRandomUUID() |
| |
| |
| def get_characteristic_uuid(): |
| """Generates a random Characteristic UUID from a set of functions. |
| |
| Similar to get_service_uuid() but weights get_characteristic_uuid_from_fakes() |
| more heavily to increase the probability of generating test pages that can |
| interact with the fake adapters. |
| |
| See get_characteristic_uuid_from_fakes(), get_valid_characteristic_alias() and |
| GetRandomUUID() for the different values this function can return. |
| |
| Returns: |
| A string or a number that can be used as a Service UUID by the Web |
| Bluetooth API. |
| """ |
| roll = random.random() |
| if roll < 0.8: |
| return get_characteristic_uuid_from_fakes() |
| elif roll < 0.9: |
| return get_valid_characteristic_alias() |
| else: |
| return GetRandomUUID() |
| |
| |
| def GetRequestDeviceOptions(): |
| """Returns an object used by navigator.bluetooth.requestDevice.""" |
| # TODO(ortuno): Randomize the members, number of filters, services, etc. |
| |
| return '{filters: [{services: [ %s ]}]}' % GetAdvertisedServiceUUID() |
| |
| |
| def GetBasicBase(): |
| """Returns a string that sets a random fake adapter.""" |
| adapter = _ToJsStr(random.choice(wbt_fakes.ALL_ADAPTERS)) |
| return BASIC_BASE.format(fake_adapter_name=adapter) |
| |
| |
| def GetDeviceDiscoveryBase(): |
| """Generates a string that contains all steps to find a device.""" |
| adapter, services = random.choice(wbt_fakes.ADAPTERS_WITH_DEVICES) |
| return DEVICE_DISCOVERY_BASE.format( |
| fake_adapter_name=_ToJsStr(adapter), |
| service_uuid=_ToJsStr(random.choice(services))) |
| |
| |
| def GetConnectableBase(): |
| """Generates a string that contains all steps to connect to a device. |
| |
| Returns: A string that: |
| 1. Sets an adapter to a fake adapter with a connectable device. |
| 2. Looks for the connectable device. |
| 3. Connects to it. |
| """ |
| adapter, services = random.choice(wbt_fakes.ADAPTERS_WITH_DEVICES) |
| return DEVICE_DISCOVERY_BASE.format( |
| fake_adapter_name=_ToJsStr(adapter), |
| service_uuid=_ToJsStr(random.choice(services))) |
| |
| |
| def get_services_retrieved_base(): |
| """Returns a string that contains all steps to retrieve a service. |
| |
| Returns: A string that: |
| 1. Sets an adapter to a fake adapter with a connectable device with |
| services. |
| 2. Use one of the device's services to look for that device. |
| 3. Connects to it. |
| 4. Retrieve the device's service used in 2. |
| """ |
| adapter, services = random.choice(wbt_fakes.ADAPTERS_WITH_SERVICES) |
| service_uuid = _ToJsStr(random.choice(services)) |
| |
| base = random.choice([SERVICE_RETRIEVED_BASE, SERVICES_RETRIEVED_BASE]) |
| return base.format( |
| fake_adapter_name=_ToJsStr(adapter), |
| service_uuid=service_uuid, |
| optional_service_uuid=random.choice(['', service_uuid])) |
| |
| |
| def get_characteristics_retrieved_base(): |
| """Returns a string that contains all steps to retrieve a characteristic. |
| |
| Returns: A string that: |
| 1. Sets an adapter to a fake adapter with a connectable device with |
| services. |
| 2. Use one of the device's services to look for that device. |
| 3. Connects to it. |
| 4. Retrieve the device's service used in 2. |
| 5. Retrieve a characteristic from that service. |
| """ |
| adapter, services = random.choice(wbt_fakes.ADAPTERS_WITH_CHARACTERISTICS) |
| |
| service_uuid, characteristics = random.choice(services) |
| service_uuid = _ToJsStr(service_uuid) |
| |
| characteristic_uuid = _ToJsStr(random.choice(characteristics)) |
| |
| optional_service_uuid = random.choice(['', service_uuid]) |
| optional_characteristic_uuid = random.choice(['', characteristic_uuid]) |
| |
| services_base = random.choice([SERVICE_RETRIEVED_BASE, |
| SERVICES_RETRIEVED_BASE]) |
| |
| characteristics_base = services_base + random.choice([ |
| CHARACTERISTIC_RETRIEVED_BASE, |
| CHARACTERISTICS_RETRIEVED_BASE, |
| ]) |
| |
| return characteristics_base.format( |
| fake_adapter_name=_ToJsStr(adapter), |
| service_uuid=service_uuid, |
| optional_service_uuid=optional_service_uuid, |
| characteristic_uuid=characteristic_uuid, |
| optional_characteristic_uuid=optional_characteristic_uuid) |
| |
| |
| def get_get_primary_services_call(): |
| call = random.choice([u'getPrimaryService({service_uuid})', |
| u'getPrimaryServices({optional_service_uuid})']) |
| |
| return call.format( |
| service_uuid=get_service_uuid(), |
| optional_service_uuid=random.choice(['', get_service_uuid()])) |
| |
| |
| def get_characteristics_call(): |
| call = random.choice([ |
| u'getCharacteristic({characteristic_uuid})', |
| u'getCharacteristics({optional_characteristic_uuid})' |
| ]) |
| |
| return call.format( |
| characteristic_uuid=get_characteristic_uuid(), |
| optional_characteristic_uuid=random.choice( |
| ['', get_characteristic_uuid()])) |
| |
| |
| def get_pick_a_service(): |
| """Returns a string that picks a service from 'services'.""" |
| # 'services' may be defined by the GetPrimaryService(s) tokens. |
| string = \ |
| 'var service; '\ |
| 'if (typeof services !== \'undefined\') '\ |
| ' service = Array.isArray(services)'\ |
| ' ? services[{} % services.length]'\ |
| ' : services' |
| return string.format(random.randint(0, sys.maxint)) |
| |
| |
| def get_pick_a_characteristic(): |
| """Returns a string that picks a characteristic from 'characteristics'.""" |
| # 'characteristics' maybe be defined by the GetCharacteristic(s) tokens. |
| string = \ |
| 'var characteristic; '\ |
| 'if (typeof characteristics !== \'undefined\') '\ |
| ' characteristic = Array.isArray(characteristics)'\ |
| ' ? characteristics[{} % characteristics.length]'\ |
| ' : characteristics' |
| return string.format(random.randint(0, sys.maxint)) |
| |
| |
| def get_reload_id(): |
| return _ToJsStr(_get_random_number()) |
| |
| |
| def get_buffer_source(): |
| """Returns a new BufferSource. |
| https://heycam.github.io/webidl/#BufferSource |
| """ |
| |
| choice = random.choice(['ArrayBuffer', 'DataView', 'TypedArray']) |
| |
| if choice == 'ArrayBuffer': |
| # We choose 2**10 as the upper boundry because the max length allowed |
| # by WebBluetooth is 2**10. |
| return 'new ArrayBuffer({length})'.format( |
| length=utils.UniformExpoInteger(0, 10)) |
| if choice == 'DataView': |
| return 'new DataView({typed_array}.buffer)'.format( |
| typed_array=_get_typed_array()) |
| if choice == 'TypedArray': |
| return _get_typed_array() |