webkitpy: Implement device type specific expected results (Part 2)
[WebKit-https.git] / Tools / Scripts / webkitpy / xcode / device_type.py
1 # Copyright (C) 2017 Apple Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1.  Redistributions of source code must retain the above copyright
7 #     notice, this list of conditions and the following disclaimer.
8 # 2.  Redistributions in binary form must reproduce the above copyright
9 #     notice, this list of conditions and the following disclaimer in the
10 #     documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23 from webkitpy.common.version_name_map import VersionNameMap
24
25
26 # This class is designed to match device types. Because it is used for matching, 'None' is treated as a wild-card.
27 class DeviceType(object):
28
29     @classmethod
30     def from_string(cls, device_string, version=None):
31         """
32         Converts a string into a DeviceType object. These strings should be of the form
33         '<hardware_family> <hardware_type>', where <hardware_family> is mandatory and
34         <hardware_family> is optional.
35
36         Example input + output:
37         ('iPhone 6 Plus') -> DeviceType(hardware_family='iPhone', hardware_type='6 Plus', software_version=None)
38         ('iPhone', Version(11)) -> DeviceType(hardware_family='iPhone', hardware_type=None, software_version=Version(11))
39         ('Apple TV 4K') -> DeviceType(hardware_family='TV', hardware_type='4K', software_version=None)
40
41         :param device_string: String representing a device.
42         :type device_string: str
43         :param version: Version object of software run on the device.
44         :type version: Version
45         :returns: DeviceType object
46         :rtype: DeviceType
47         """
48         split_str = device_string.split(' ')
49         if len(split_str) == 1:
50             return cls(hardware_family=device_string, software_version=version)
51         family_index = 0
52         if split_str[family_index].lower() == 'apple':
53             family_index = 1
54         return cls(
55             hardware_family=split_str[family_index],
56             hardware_type=' '.join(split_str[family_index + 1:]) if len(split_str) > family_index + 1 else None,
57             software_version=version)
58
59     def _define_software_variant_from_hardware_family(self):
60         if self.hardware_family is None:
61             return
62         if self.software_variant:
63             return
64
65         self.software_variant = 'iOS'
66         if self.hardware_family.lower().split(' ')[-1].startswith('watch'):
67             self.hardware_family = 'Apple Watch'
68             self.software_variant = 'watchOS'
69         elif self.hardware_family.lower().split(' ')[-1].startswith('tv'):
70             self.hardware_family = 'Apple TV'
71             self.software_variant = 'tvOS'
72
73     def check_consistency(self):
74         if self.hardware_family is not None:
75             assert self.software_variant is not None
76             if self.hardware_family == 'Apple Watch':
77                 assert self.software_variant is 'watchOS'
78             elif self.hardware_family == 'Apple TV':
79                 assert self.software_variant == 'tvOS'
80             else:
81                 assert self.software_variant == 'iOS'
82
83         if self.hardware_type is not None:
84             assert self.hardware_family is not None
85         if self.software_version:
86             assert self.software_variant is not None
87
88     def __init__(self, hardware_family=None, hardware_type=None, software_version=None, software_variant=None):
89         """
90         :param hardware_family: iPhone, iPad, Apple Watch and Apple TV are all examples.
91         :type hardware_family: str
92         :param hardware_type: 6s, Series 2 - 42mm, 4k are all examples
93         :type hardware_type: str
94         :param software_version: Version object representing software the device is running.
95         :type software_version: Version
96         :param software_variant: Groups together hardware families which share an OS, like iPad and iPhone. iOS, tvOS and watchOS are examples.
97         :type software_variant: str
98         """
99         if hardware_family is None and hardware_type is None and software_version is None and software_variant is None:
100             raise ValueError('Cannot instantiate DeviceType with no arguments')
101
102         self.hardware_family = hardware_family
103         self.hardware_type = hardware_type
104         self.software_version = software_version
105         self.software_variant = software_variant
106
107         self._define_software_variant_from_hardware_family()
108         self.check_consistency()
109
110     def __str__(self):
111         return '{hardware_family}{hardware_type} running {version}'.format(
112             hardware_family=self.hardware_family if self.hardware_family else 'Device',
113             hardware_type=' {}'.format(self.hardware_type) if self.hardware_type else '',
114             version=VersionNameMap.map().to_name(self.software_version, platform=self.software_variant.lower()) if self.software_version else self.software_variant,
115         )
116
117     # This technique of matching treats 'None' a wild-card.
118     def __eq__(self, other):
119         assert isinstance(other, DeviceType)
120         if self.hardware_family is not None and other.hardware_family is not None and self.hardware_family.lower() != other.hardware_family.lower():
121             return False
122         if self.hardware_type is not None and other.hardware_type is not None and self.hardware_type.lower() != other.hardware_type.lower():
123             return False
124         if self.software_variant is not None and other.software_variant is not None and self.software_variant.lower() != other.software_variant.lower():
125             return False
126         if self.software_version is not None and other.software_version is not None and self.software_version != other.software_version:
127             return False
128         return True
129
130     def __contains__(self, other):
131         assert isinstance(other, DeviceType)
132         if self.hardware_family is not None and (not other.hardware_family or self.hardware_family.lower() != other.hardware_family.lower()):
133             return False
134         if self.hardware_type is not None and (not other.hardware_type or self.hardware_type.lower() != other.hardware_type.lower()):
135             return False
136         if self.software_variant is not None and (not other.software_variant or self.software_variant.lower() != other.software_variant.lower()):
137             return False
138         if self.software_version is not None and other.software_version is not None and not other.software_version in self.software_version:
139             return False
140         return True
141
142     def __hash__(self):
143         return hash((self.hardware_family, self.hardware_type, self.software_variant, self.software_version))