packages feed

ua-parser 0.2.1 → 0.3

raw patch · 4 files changed

+586/−147 lines, 4 filesdep +HUnitdep +criteriondep +deepseqdep ~base

Dependencies added: HUnit, criterion, deepseq, derive, test-framework, test-framework-hunit, test-framework-quickcheck2

Dependency ranges changed: base

Files

regexes.yaml view
@@ -1,9 +1,10 @@ user_agent_parsers:   #### SPECIAL CASES TOP #### -  # must go before Opera-  - regex: '^(Opera)/(\d+)\.(\d+) \(Nintendo Wii'-    family_replacement: 'Wii'+  # HbbTV standard defines what features the browser should understand.+  # but it's like targeting "HTML5 browsers", effective browser support depends on the model+  # See os_parsers if you want to target a specific TV+  - regex: '(HbbTV)/(\d+)\.(\d+)\.(\d+) \('    # must go before Firefox to catch SeaMonkey/Camino   - regex: '(SeaMonkey|Camino)/(\d+)\.(\d+)\.?([ab]?\d+[a-z]*)'@@ -65,17 +66,25 @@   - regex: '(Opera Mini)/att/(\d+)\.(\d+)'   - regex: '(Opera)/9.80.*Version/(\d+)\.(\d+)(?:\.(\d+))?' +  # Opera 14 for Android uses a WebKit render engine.+  - regex: '(?:Mobile Safari).*(OPR)/(\d+)\.(\d+)\.(\d+)'+    family_replacement: 'Opera Mobile'++  # Opera 15 for Desktop is similar to Chrome but includes an "OPR" Version string.+  - regex: '(?:Chrome).*(OPR)/(\d+)\.(\d+)\.(\d+)'+    family_replacement: 'Opera'+   # Palm WebOS looks a lot like Safari.-  - regex: '(webOSBrowser)/(\d+)\.(\d+)'-  - regex: '(webOS)/(\d+)\.(\d+)'-    family_replacement: 'webOSBrowser'-  - regex: '(wOSBrowser).+TouchPad/(\d+)\.(\d+)'-    family_replacement: 'webOS TouchPad'+  - regex: '(hpw|web)OS/(\d+)\.(\d+)(?:\.(\d+))?'+    family_replacement: 'webOS Browser'    # LuaKit has no version info.   # http://luakit.org/projects/luakit/   - regex: '(luakit)'     family_replacement: 'LuaKit'+    +  # Snowshoe+  - regex: '(Snowshoe)/(\d+)\.(\d+).(\d+)'    # Lightning (for Thunderbird)   # http://www.mozilla.org/projects/calendar/lightning/@@ -88,6 +97,8 @@     family_replacement: 'Swiftfox'    # Rekonq+  - regex: '(rekonq)/(\d+)\.(\d+)\.?(\d+)? Safari'+    family_replacement: 'Rekonq'   - regex: 'rekonq'     family_replacement: 'Rekonq' @@ -108,9 +119,6 @@   # Bots   - regex: '(YottaaMonitor|BrowserMob|HttpMonitor|YandexBot|Slurp|BingPreview|PagePeeker|ThumbShotsBot|WebThumb|URL2PNG|ZooShot|GomezA|Catchpoint bot|Willow Internet Crawler|Google SketchUp|Read%20Later)' -  # must go before NetFront below-  - regex: '(Kindle)/(\d+)\.(\d+)'-   - regex: '(Symphony) (\d+).(\d+)'    - regex: '(Minimo)'@@ -128,20 +136,23 @@     family_replacement: 'Chrome Frame'    # UC Browser-  - regex: '(UC Browser)(\d+)\.(\d+)\.(\d+)'+  - regex: '(UCBrowser)[ /](\d+)\.(\d+)\.(\d+)'+    family_replacement: 'UC Browser'+  - regex: '(UC Browser)[ /](\d+)\.(\d+)\.(\d+)'+  - regex: '(UC Browser|UCBrowser|UCWEB)(\d+)\.(\d+)\.(\d+)'+    family_replacement: 'UC Browser'    # Tizen Browser (second case included in browser/major.minor regex)   - regex: '(SLP Browser)/(\d+)\.(\d+)'     family_replacement: 'Tizen Browser' -  # Epiphany browser (identifies as Chromium)-  - regex: '(Epiphany)/(\d+)\.(\d+).(\d+)'-   # Sogou Explorer 2.X   - regex: '(SE 2\.X) MetaSr (\d+)\.(\d+)'     family_replacement: 'Sogou Explorer'-    -  # Baidu Browser++  # Baidu Browsers (desktop spoofs chrome & IE, explorer is mobile)+  - regex: '(baidubrowser)[/\s](\d+)'+    family_replacement: 'Baidu Browser'   - regex: '(FlyFlow)/(\d+)\.(\d+)'     family_replacement: 'Baidu Explorer' @@ -157,24 +168,48 @@   - regex: '(Twitterbot)/(\d+)\.(\d+)'     family_replacement: 'TwitterBot' +  # Rackspace Monitoring+  - regex: '(Rackspace Monitoring)/(\d+)\.(\d+)'+    family_replacement: 'RackspaceBot'+   # PyAMF   - regex: '(PyAMF)/(\d+)\.(\d+)\.(\d+)' +  # Yandex Browser+  - regex: '(YaBrowser)/(\d+)\.(\d+)\.(\d+)'+    family_replacement: 'Yandex Browser'++  # Mail.ru Amigo/Internet Browser (Chromium-based)+  - regex: '(Chrome)/(\d+)\.(\d+)\.(\d+).* MRCHROME'+    family_replacement: 'Mail.ru Chromium Browser'++  # AOL Browser (IE-based)+  - regex: '(AOL) (\d+)\.(\d+); AOLBuild (\d+)'+   #### END SPECIAL CASES TOP ####    #### MAIN CASES - this catches > 50% of all browsers ####    # Browser/major_version.minor_version.beta_version-  - regex: '(AdobeAIR|Chromium|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Shiira|Sunrise|Chrome|Flock|Netscape|Lunascape|WebPilot|Vodafone|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\.Browser|Bunjaloo|Google Earth|Raven for Mac)/(\d+)\.(\d+)\.(\d+)'+  - regex: '(AdobeAIR|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Epiphany|Shiira|Sunrise|Flock|Netscape|Lunascape|WebPilot|Vodafone|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\.Browser|Bunjalloo|Google Earth|Raven for Mac|Openwave)/(\d+)\.(\d+)\.(\d+)' +  # Chrome/Chromium/major_version.minor_version.beta_version+  - regex: '(Chromium|Chrome)/(\d+)\.(\d+)\.(\d+)'+   # Browser/major_version.minor_version-  - regex: '(Bolt|Jasmine|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Chrome|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|Vodafone|NetFront|Netfront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|NetNewsWire|Space Bison|Stainless|Orca|Dolfin|BOLT|Minimo|Tizen Browser|Polaris|Abrowser)/(\d+)\.(\d+)'+  - regex: '(Bolt|Jasmine|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Chrome|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|Vodafone|NetFront|Netfront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|NetNewsWire|Space Bison|Stainless|Orca|Dolfin|BOLT|Minimo|Tizen Browser|Polaris|Abrowser|Planetweb|ICE Browser)/(\d+)\.(\d+)' +  # Chrome/Chromium/major_version.minor_version+  - regex: '(Chromium|Chrome)/(\d+)\.(\d+)'+   # Browser major_version.minor_version.beta_version (space instead of slash)   - regex: '(iRider|Crazy Browser|SkipStone|iCab|Lunascape|Sleipnir|Maemo Browser) (\d+)\.(\d+)\.(\d+)'   # Browser major_version.minor_version (space instead of slash)-  - regex: '(iCab|Lunascape|Opera|Android|Jasmine|Polaris|BREW) (\d+)\.(\d+)\.?(\d+)?'-+  - regex: '(iCab|Lunascape|Opera|Android|Jasmine|Polaris) (\d+)\.(\d+)\.?(\d+)?'+  +  # Kindle WebKit+  - regex: '(Kindle)/(\d+)\.(\d+)'+     # weird android UAs   - regex: '(Android) Donut'     v1_replacement: '1'@@ -213,18 +248,32 @@   - regex: '(Obigo)InternetBrowser'   - regex: '(Obigo)\-Browser'   - regex: '(Obigo|OBIGO)[^\d]*(\d+)(?:.(\d+))?'+    family_replacement: 'Obigo'    - regex: '(MAXTHON|Maxthon) (\d+)\.(\d+)'     family_replacement: 'Maxthon'   - regex: '(Maxthon|MyIE2|Uzbl|Shiira)'     v1_replacement: '0' -  - regex: '(PLAYSTATION) (\d+)'-    family_replacement: 'PlayStation'-  - regex: '(PlayStation Portable)[^\d]+(\d+).(\d+)'+  - regex: 'PLAYSTATION 3.+WebKit'+    family_replacement: 'NetFront NX'+  - regex: 'PLAYSTATION 3'+    family_replacement: 'NetFront'+  - regex: '(PlayStation Portable)'+    family_replacement: 'NetFront'+  - regex: '(PlayStation Vita)'+    family_replacement: 'NetFront NX'+  +  - regex: 'AppleWebKit.+ (NX)/(\d+)\.(\d+)\.(\d+)'+    family_replacement: 'NetFront NX'+  - regex: '(Nintendo 3DS)'+    family_replacement: 'NetFront NX'    - regex: '(BrowseX) \((\d+)\.(\d+)\.(\d+)' +  - regex: '(NCSA_Mosaic)/(\d+)\.(\d+)'+    family_replacement: 'NCSA Mosaic'+   # Polaris/d.d is above   - regex: '(POLARIS)/(\d+)\.(\d+)'     family_replacement: 'Polaris'@@ -234,6 +283,9 @@   - regex: '(BonEcho)/(\d+)\.(\d+)\.(\d+)'     family_replacement: 'Bon Echo' +  - regex: 'M?QQBrowser'+    family_replacement: 'QQ Browser'+   - regex: '(iPod).+Version/(\d+)\.(\d+)\.(\d+)'     family_replacement: 'Mobile Safari'   - regex: '(iPod).*Version/(\d+)\.(\d+)'@@ -252,36 +304,50 @@     family_replacement: 'Mobile Safari'    - regex: '(AvantGo) (\d+).(\d+)'+  +  - regex: '(OneBrowser)/(\d+).(\d+)'+    family_replacement: 'ONE Browser'    - regex: '(Avant)'     v1_replacement: '1' +  # This is the Tesla Model S (see similar entry in device parsers)+  - regex: '(QtCarBrowser)'+    v1_replacement: '1'++  - regex: '(iBrowser/Mini)(\d+).(\d+)'+    family_replacement: 'iBrowser Mini'   # nokia browsers   # based on: http://www.developer.nokia.com/Community/Wiki/User-Agent_headers_for_Nokia_devices   - regex: '^(Nokia)'     family_replacement: 'Nokia Services (WAP) Browser'   - regex: '(NokiaBrowser)/(\d+)\.(\d+).(\d+)\.(\d+)'+    family_replacement: 'Nokia Browser'   - regex: '(NokiaBrowser)/(\d+)\.(\d+).(\d+)'+    family_replacement: 'Nokia Browser'   - regex: '(NokiaBrowser)/(\d+)\.(\d+)'+    family_replacement: 'Nokia Browser'   - regex: '(BrowserNG)/(\d+)\.(\d+).(\d+)'-    family_replacement: 'NokiaBrowser'+    family_replacement: 'Nokia Browser'   - regex: '(Series60)/5\.0'-    family_replacement: 'NokiaBrowser'+    family_replacement: 'Nokia Browser'     v1_replacement: '7'     v2_replacement: '0'   - regex: '(Series60)/(\d+)\.(\d+)'     family_replacement: 'Nokia OSS Browser'   - regex: '(S40OviBrowser)/(\d+)\.(\d+)\.(\d+)\.(\d+)'-    family_replacement: 'Nokia Series 40 Ovi Browser'+    family_replacement: 'Ovi Browser'   - regex: '(Nokia)[EN]?(\d+)'    # BlackBerry devices+  - regex: '(BB10);'+    family_replacement: 'BlackBerry WebKit'   - regex: '(PlayBook).+RIM Tablet OS (\d+)\.(\d+)\.(\d+)'-    family_replacement: 'Blackberry WebKit'+    family_replacement: 'BlackBerry WebKit'   - regex: '(Black[bB]erry).+Version/(\d+)\.(\d+)\.(\d+)'-    family_replacement: 'Blackberry WebKit'+    family_replacement: 'BlackBerry WebKit'   - regex: '(Black[bB]erry)\s?(\d+)'-    family_replacement: 'Blackberry'+    family_replacement: 'BlackBerry'    - regex: '(OmniWeb)/v(\d+)\.(\d+)' @@ -291,6 +357,9 @@   - regex: '(Pre)/(\d+)\.(\d+)'     family_replacement: 'Palm Pre' +  # fork of Links+  - regex: '(ELinks)/(\d+)\.(\d+)'+  - regex: '(ELinks) \((\d+)\.(\d+)'   - regex: '(Links) \((\d+)\.(\d+)'    - regex: '(QtWeb) Internet Browser/(\d+)\.(\d+)'@@ -300,7 +369,11 @@    # Amazon Silk, should go before Safari   - regex: '(Silk)/(\d+)\.(\d+)(?:\.([0-9\-]+))?'+    family_replacement: 'Amazon Silk' +  # Phantomjs, should go before Safari+  - regex: '(PhantomJS)/(\d+)\.(\d+)\.(\d+)'+   # WebKit Nightly   - regex: '(AppleWebKit)/(\d+)\.?(\d+)?\+ .* Safari'     family_replacement: 'WebKit Nightly'@@ -320,14 +393,78 @@    - regex: '(Teleca)'     family_replacement: 'Teleca Browser'+    +  - regex: '(Phantom)/V(\d+)\.(\d+)'+    family_replacement: 'Phantom Browser' -  - regex: '(MSIE) (\d+)\.(\d+)'+  - regex: '([MS]?IE) (\d+)\.(\d+)'     family_replacement: 'IE' -  - regex: '(Nintendo 3DS).* Version/(\d+)\.(\d+)(?:\.(\w+))'+  - regex: 'Trident(.*)rv.(\d+)\.(\d+)'+    family_replacement: 'IE' +  - regex: '(python-requests)/(\d+)\.(\d+)'+    family_replacement: 'Python Requests'+ os_parsers:+  ##########+  # HbbTV vendors+  ########## +  # starts with the easy one : Panasonic seems consistent across years, hope it will continue+  #HbbTV/1.1.1 (;Panasonic;VIERA 2011;f.532;0071-0802 2000-0000;)+  #HbbTV/1.1.1 (;Panasonic;VIERA 2012;1.261;0071-3103 2000-0000;)+  #HbbTV/1.2.1 (;Panasonic;VIERA 2013;3.672;4101-0003 0002-0000;)+  #- regex: 'HbbTV/\d+\.\d+\.\d+ \(;(Panasonic);VIERA ([0-9]{4});'++  # Sony is consistent too but do not place year like the other+  # Opera/9.80 (Linux armv7l; HbbTV/1.1.1 (; Sony; KDL32W650A; PKG3.211EUA; 2013;); ) Presto/2.12.362 Version/12.11+  # Opera/9.80 (Linux mips; U;  HbbTV/1.1.1 (; Sony; KDL40HX751; PKG1.902EUA; 2012;);; en) Presto/2.10.250 Version/11.60+  # Opera/9.80 (Linux mips; U;  HbbTV/1.1.1 (; Sony; KDL22EX320; PKG4.017EUA; 2011;);; en) Presto/2.7.61 Version/11.00+  #- regex: 'HbbTV/\d+\.\d+\.\d+ \(; (Sony);.*;.*; ([0-9]{4});\)'+++  # LG is consistent too, but we need to add manually the year model+  #Mozilla/5.0 (Unknown; Linux armv7l) AppleWebKit/537.1+ (KHTML, like Gecko) Safari/537.1+ HbbTV/1.1.1 ( ;LGE ;NetCast 4.0 ;03.20.30 ;1.0M ;)+  #Mozilla/5.0 (DirectFB; Linux armv7l) AppleWebKit/534.26+ (KHTML, like Gecko) Version/5.0 Safari/534.26+ HbbTV/1.1.1 ( ;LGE ;NetCast 3.0 ;1.0 ;1.0M ;)+  - regex: 'HbbTV/\d+\.\d+\.\d+ \( ;(LG)E ;NetCast 4.0'+    os_v1_replacement: '2013'+  - regex: 'HbbTV/\d+\.\d+\.\d+ \( ;(LG)E ;NetCast 3.0'+    os_v1_replacement: '2012'++  # Samsung is on its way of normalizing their user-agent+  # HbbTV/1.1.1 (;Samsung;SmartTV2013;T-FXPDEUC-1102.2;;) WebKit+  # HbbTV/1.1.1 (;Samsung;SmartTV2013;T-MST12DEUC-1102.1;;) WebKit+  # HbbTV/1.1.1 (;Samsung;SmartTV2012;;;) WebKit+  # HbbTV/1.1.1 (;;;;;) Maple_2011+  - regex: 'HbbTV/1.1.1 \(;;;;;\) Maple_2011'+    os_replacement: 'Samsung'+    os_v1_replacement: '2011'+  # manage the two models of 2013+  - regex: 'HbbTV/\d+\.\d+\.\d+ \(;(Samsung);SmartTV([0-9]{4});.*FXPDEUC'+    os_v2_replacement: 'UE40F7000'+  - regex: 'HbbTV/\d+\.\d+\.\d+ \(;(Samsung);SmartTV([0-9]{4});.*MST12DEUC'+    os_v2_replacement: 'UE32F4500'+  # generic Samsung (works starting in 2012)+  #- regex: 'HbbTV/\d+\.\d+\.\d+ \(;(Samsung);SmartTV([0-9]{4});'++  # Philips : not found any other way than a manual mapping+  # Opera/9.80 (Linux mips; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/4.1.3 PHILIPSTV/1.1.1; en) Presto/2.10.250 Version/11.60+  # Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.2.1; en) Presto/2.6.33 Version/10.70+  - regex: 'HbbTV/1.1.1 \(; (Philips);.*NETTV/4'+    os_v1_replacement: '2013'+  - regex: 'HbbTV/1.1.1 \(; (Philips);.*NETTV/3'+    os_v1_replacement: '2012'+  - regex: 'HbbTV/1.1.1 \(; (Philips);.*NETTV/2'+    os_v1_replacement: '2011'++  # the HbbTV emulator developers use HbbTV/1.1.1 (;;;;;) firetv-firefox-plugin 1.1.20+  - regex: 'HbbTV/\d+\.\d+\.\d+.*(firetv)-firefox-plugin (\d+).(\d+).(\d+)'+    os_replacement: 'FireHbbTV'++  # generic HbbTV, hoping to catch manufacturer name (always after 2nd comma) and the first string that looks like a 2011-2019 year+  - regex: 'HbbTV/\d+\.\d+\.\d+ \(.*; ?([a-zA-Z]+) ?;.*(201[1-9]).*\)'+   ##########   # Android   # can actually detect rooted android os. do we care?@@ -355,6 +492,12 @@     os_v1_replacement: '3'    ##########+  # Kindle Android+  ##########+  - regex: '(Silk-Accelerated=[a-z]{4,5})'+    os_replacement: 'Android'++  ##########   # Windows   # http://en.wikipedia.org/wiki/Windows_NT#Releases   # possibility of false positive when different marketing names share same NT kernel@@ -362,7 +505,6 @@   # lots of ua strings have Windows NT 4.1 !?!?!?!? !?!? !? !????!?! !!! ??? !?!?! ?   # (very) roughly ordered in terms of frequency of occurence of regex (win xp currently most frequent, etc)   ##########-  - regex: '(Windows Phone 6\.5)'    - regex: '(Windows (?:NT 5\.2|NT 5\.1))'     os_replacement: 'Windows XP'@@ -370,7 +512,7 @@   # ie mobile des ktop mode   # spoofs nt 6.1. must come before windows 7   - regex: '(XBLWP7)'-    os_replacement: 'Windows Phone OS'+    os_replacement: 'Windows Phone'    - regex: '(Windows NT 6\.1)'     os_replacement: 'Windows 7'@@ -378,8 +520,14 @@   - regex: '(Windows NT 6\.0)'     os_replacement: 'Windows Vista' -  - regex: '(Windows 98|Windows XP|Windows ME|Windows 95|Windows CE|Windows 7|Windows NT 4\.0|Windows Vista|Windows 2000)'+  - regex: '(Win 9x 4\.90)'+    os_replacement: 'Windows Me' +  - regex: '(Windows 98|Windows XP|Windows ME|Windows 95|Windows CE|Windows 7|Windows NT 4\.0|Windows Vista|Windows 2000|Windows 3.1)'++  - regex: '(Windows NT 6\.2; ARM;)'+    os_replacement: 'Windows RT'+   # is this a spoof or is nt 6.2 out and about in some capacity?   - regex: '(Windows NT 6\.2)'     os_replacement: 'Windows 8'@@ -387,8 +535,8 @@   - regex: '(Windows NT 5\.0)'     os_replacement: 'Windows 2000' -  - regex: '(Windows Phone OS) (\d+)\.(\d+)'-+  - regex: '(Windows Phone) (\d+)\.(\d+)'+  - regex: '(Windows Phone) OS (\d+)\.(\d+)'   - regex: '(Windows ?Mobile)'     os_replacement: 'Windows Mobile' @@ -404,13 +552,15 @@   ##########   - regex: '(Tizen)/(\d+)\.(\d+)' --   ##########   # Mac OS   # http://en.wikipedia.org/wiki/Mac_OS_X#Versions   ##########   - regex: '(Mac OS X) (\d+)[_.](\d+)(?:[_.](\d+))?'+  +  # IE on Mac doesn't specify version number+  - regex: 'Mac_PowerPC'+    os_replacement: 'Mac OS'    # builds before tiger don't seem to specify version? @@ -434,8 +584,6 @@    - regex: '(AppleTV)/(\d+)\.(\d+)'     os_replacement: 'ATV OS X'-    os_v1_replacement: '$1'-    os_v2_replacement: '$2'    ##########   # Chrome OS@@ -449,9 +597,10 @@   ##########   # Linux distros   ##########-  - regex: '(Debian)-(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?'+  - regex: '([Dd]ebian)'+    os_replacement: 'Debian'   - regex: '(Linux Mint)(?:/(\d+))?'-  - regex: '(Mandriva)(?: Linux)?/(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?'+  - regex: '(Mandriva)(?: Linux)?/(?:[\d.-]+m[a-z]{2}(\d+).(\d))?'    ##########   # Symbian + Symbian OS@@ -470,10 +619,14 @@   - regex: '(MeeGo)'   - regex: 'Symbian [Oo][Ss]'     os_replacement: 'Symbian OS'+  - regex: 'Series40;'+    os_replacement: 'Nokia Series 40'    ##########   # BlackBerry devices   ##########+  - regex: '(BB10);.+Version/(\d+)\.(\d+)\.(\d+)'+    os_replacement: 'BlackBerry OS'   - regex: '(Black[Bb]erry)[0-9a-z]+/(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?'     os_replacement: 'BlackBerry OS'   - regex: '(Black[Bb]erry).+Version/(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?'@@ -483,31 +636,61 @@   - regex: '(Play[Bb]ook)'     os_replacement: 'BlackBerry Tablet OS'   - regex: '(Black[Bb]erry)'-    os_replacement: 'Blackberry OS'+    os_replacement: 'BlackBerry OS'    ##########+  # Firefox OS+  ##########+  - regex: '\(Mobile;.+Firefox/\d+\.\d+'+    os_replacement: 'Firefox OS'++  ##########+  # BREW+  # yes, Brew is lower-cased for Brew MP+  ##########+  - regex: '(BREW)[ /](\d+)\.(\d+)\.(\d+)'+  - regex: '(BREW);'+  - regex: '(Brew MP|BMP)[ /](\d+)\.(\d+)\.(\d+)'+    os_replacement: 'Brew MP'+  - regex: 'BMP;'+    os_replacement: 'Brew MP'++  ##########+  # Google TV+  ##########+  - regex: '(GoogleTV) (\d+)\.(\d+)\.(\d+)'+  # Old style+  - regex: '(GoogleTV)/[\da-z]+'++  - regex: '(WebTV)/(\d+).(\d+)'+  +  ##########   # Misc mobile   ##########-  - regex: '(webOS|hpwOS)/(\d+)\.(\d+)(?:\.(\d+))?'+  - regex: '(hpw|web)OS/(\d+)\.(\d+)(?:\.(\d+))?'     os_replacement: 'webOS'+  - regex: '(VRE);'    ##########   # Generic patterns   # since the majority of os cases are very specific, these go last   ##########   # first.second.third.fourth bits-  - regex: '(SUSE|Fedora|Red Hat|PCLinuxOS)/(\d+)\.(\d+)\.(\d+)\.(\d+)'+  - regex: '(Fedora|Red Hat|PCLinuxOS)/(\d+)\.(\d+)\.(\d+)\.(\d+)'    # first.second.third bits-  - regex: '(SUSE|Fedora|Red Hat|Puppy|PCLinuxOS|CentOS)/(\d+)\.(\d+)\.(\d+)'+  - regex: '(Red Hat|Puppy|PCLinuxOS)/(\d+)\.(\d+)\.(\d+)'    # first.second bits   - regex: '(Ubuntu|Kindle|Bada|Lubuntu|BackTrack|Red Hat|Slackware)/(\d+)\.(\d+)'-  - regex: '(PlayStation Vita) (\d+)\.(\d+)'    # just os-  - regex: '(Windows|OpenBSD|FreeBSD|NetBSD|Ubuntu|Kubuntu|Android|Arch Linux|CentOS|WeTab|Slackware)'+  - regex: '(Windows|OpenBSD|FreeBSD|NetBSD|Android|WeTab)'+  - regex: '(Ubuntu|Kubuntu|Arch Linux|CentOS|Slackware|Gentoo|openSUSE|SUSE|Red Hat|Fedora|PCLinuxOS|Gentoo|Mageia)'+  - regex: '(Linux)/(\d+)\.(\d+)'   - regex: '(Linux|BSD)'+  - regex: 'SunOS'+    os_replacement: 'Solaris'  device_parsers:   ##########@@ -538,16 +721,16 @@     device_replacement: 'HTC $1 (Sprint)'   - regex: 'HTC ([A-Za-z0-9]+ [A-Z])'     device_replacement: 'HTC $1'-  - regex: 'HTC-([A-Za-z0-9]+)'-    device_replacement: 'HTC $1'-  - regex: 'HTC_([A-Za-z0-9]+)'-    device_replacement: 'HTC $1'-  - regex: 'HTC ([A-Za-z0-9]+)'+  - regex: 'HTC[-_/\s]([A-Za-z0-9]+)'     device_replacement: 'HTC $1'   - regex: '(ADR[A-Za-z0-9]+)'     device_replacement: 'HTC $1'   - regex: '(HTC)' +  # Tesla Model S+  - regex: '(QtCarBrowser)'+    device_replacement: 'Tesla Model S'+   # Samsung   - regex: '(SamsungSGHi560)'     device_replacement: 'Samsung SGHi560'@@ -558,14 +741,43 @@   - regex: 'SonyEricsson([A-Za-z0-9]+)/'     device_replacement: 'Ericsson $1' +  ##########+  # PlayStation+  # The Vita spoofs the Kindle+  ##########+  - regex: 'PLAYSTATION 3'+    device_replacement: 'PlayStation 3'+  - regex: '(PlayStation Portable)'+  - regex: '(PlayStation Vita)'++  ##########+  # incomplete!+  # Kindle+  # http://amazonsilk.wordpress.com/useful-bits/silk-user-agent/+  ##########+  - regex: '(KFOT Build)'+    device_replacement: 'Kindle Fire'+  - regex: '(KFTT Build)'+    device_replacement: 'Kindle Fire HD'+  - regex: '(KFJWI Build)'+    device_replacement: 'Kindle Fire HD 8.9" WiFi'+  - regex: '(KFJWA Build)'+    device_replacement: 'Kindle Fire HD 8.9" 4G'+  - regex: '(Kindle Fire)'+  - regex: '(Kindle)'+  - regex: '(Silk)/(\d+)\.(\d+)(?:\.([0-9\-]+))?'+    device_replacement: 'Kindle Fire'+         #########   # Android General Device Matching (far from perfect)   #########-  - regex: 'Android[\- ][\d]+\.[\d]+\; [A-Za-z]{2}\-[A-Za-z]{2}\; WOWMobile (.+) Build'-  - regex: 'Android[\- ][\d]+\.[\d]+\.[\d]+; [A-Za-z]{2}\-[A-Za-z]{2}\; (.+) Build'-  - regex: 'Android[\- ][\d]+\.[\d]+\-update1\; [A-Za-z]{2}\-[A-Za-z]{2}\; (.+) Build'-  - regex: 'Android[\- ][\d]+\.[\d]+\; [A-Za-z]{2}\-[A-Za-z]{2}\; (.+) Build'+  - regex: 'Android[\- ][\d]+\.[\d]+; [A-Za-z]{2}\-[A-Za-z]{0,2}; WOWMobile (.+) Build'+  - regex: 'Android[\- ][\d]+\.[\d]+\-update1; [A-Za-z]{2}\-[A-Za-z]{0,2}; (.+) Build'+  - regex: 'Android[\- ][\d]+\.[\d]+\.[\d]+; [A-Za-z]{2}\-[A-Za-z]{0,2}; (.+) Build'+  - regex: 'Android[\- ][\d]+\.[\d]+\.[\d]+;[A-Za-z]{2}\-[A-Za-z]{0,2};(.+) Build'+  - regex: 'Android[\- ][\d]+\.[\d]+; [A-Za-z]{2}\-[A-Za-z]{0,2}; (.+) Build'   - regex: 'Android[\- ][\d]+\.[\d]+\.[\d]+; (.+) Build'+  - regex: 'Android[\- ][\d]+\.[\d]+; (.+) Build'    ##########   # NOKIA@@ -573,6 +785,8 @@   ##########   - regex: 'NokiaN([0-9]+)'     device_replacement: 'Nokia N$1'+  - regex: 'NOKIA([A-Za-z0-9\v-]+)'+    device_replacement: 'Nokia $1'   - regex: 'Nokia([A-Za-z0-9\v-]+)'     device_replacement: 'Nokia $1'   - regex: 'NOKIA ([A-Za-z0-9\-]+)'@@ -585,14 +799,19 @@     device_replacement: 'Nokia'    ##########-  # Blackberry+  # BlackBerry   # http://www.useragentstring.com/pages/BlackBerry/   ##########+  - regex: 'BB10; ([A-Za-z0-9\- ]+)\)'+    device_replacement: 'BlackBerry $1'   - regex: '(PlayBook).+RIM Tablet OS'-    device_replacement: 'Blackberry Playbook'-  - regex: '(Black[Bb]erry [0-9]+);'+    device_replacement: 'BlackBerry Playbook'+  - regex: 'Black[Bb]erry ([0-9]+);'+    device_replacement: 'BlackBerry $1'   - regex: 'Black[Bb]erry([0-9]+)'     device_replacement: 'BlackBerry $1'+  - regex: 'Black[Bb]erry;'+    device_replacement: 'BlackBerry'    ##########   # PALM / HP@@ -602,8 +821,8 @@     device_replacement: 'Palm Pre'   - regex: '(Pixi)/(\d+)\.(\d+)'     device_replacement: 'Palm Pixi'-  - regex: '(Touchpad)/(\d+)\.(\d+)'-    device_replacement: 'HP Touchpad'+  - regex: '(Touch[Pp]ad)/(\d+)\.(\d+)'+    device_replacement: 'HP TouchPad'   - regex: 'HPiPAQ([A-Za-z0-9]+)/(\d+).(\d+)'     device_replacement: 'HP iPAQ $1'   - regex: 'Palm([A-Za-z0-9]+)'@@ -614,21 +833,6 @@     device_replacement: 'HP Veer'    ##########-  # PlayStation-  ##########-  - regex: '(PlayStation Portable)'-  - regex: '(PlayStation Vita)'-  -  ##########-  # incomplete!-  # Kindle-  ##########-  - regex: '(Kindle Fire)'-  - regex: '(Kindle)'-  - regex: '(Silk)/(\d+)\.(\d+)(?:\.([0-9\-]+))?'-    device_replacement: 'Kindle Fire'--  ##########   # AppleTV   # No built in browser that I can tell   # Stack Overflow indicated iTunes-AppleTV/4.1 as a known UA for app available and I'm seeing it in live traffic@@ -637,6 +841,16 @@     device_replacement: 'AppleTV'    ##########+  # Catch the google mobile crawler before checking for iPhones.+  ##########++  - regex: 'AdsBot-Google-Mobile'+    device_replacement: 'Spider'+   +  - regex: 'Googlebot-Mobile/(\d+).(\d+)'+    device_replacement: 'Spider'+    +  ##########   # complete but probably catches spoofs   # iSTUFF   ##########@@ -644,6 +858,7 @@   # cannot determine specific device type from ua string. (3g, 3gs, 4, etc)   - regex: '(iPad) Simulator;'   - regex: '(iPad);'+  - regex: '(iPod) touch;'   - regex: '(iPod);'   - regex: '(iPhone) Simulator;'   - regex: '(iPhone);'@@ -656,7 +871,15 @@   - regex: 'acer_([A-Za-z0-9]+)_'     device_replacement: 'Acer $1' +  ##########   +  # Alcatel   ##########+  - regex: 'ALCATEL-([A-Za-z0-9]+)'+    device_replacement: 'Alcatel $1'+  - regex: 'Alcatel-([A-Za-z0-9]+)'+    device_replacement: 'Alcatel $1'++  ##########   # Amoi   ##########   - regex: 'Amoi\-([A-Za-z0-9]+)'@@ -693,14 +916,18 @@   ##########   - regex: 'DoCoMo/2\.0 ([A-Za-z0-9]+)'     device_replacement: 'DoCoMo $1'-  - regex: '([A-Za-z0-9]+)\_W\;FOMA'+  - regex: '([A-Za-z0-9]+)_W\;FOMA'     device_replacement: 'DoCoMo $1'   - regex: '([A-Za-z0-9]+)\;FOMA'     device_replacement: 'DoCoMo $1'    ##########-  # Huawei Vodafone+  # Huawei   ##########+  - regex: 'Huawei([A-Za-z0-9]+)'+    device_replacement: 'Huawei $1'+  - regex: 'HUAWEI-([A-Za-z0-9]+)'+    device_replacement: 'Huawei $1'   - regex: 'vodafone([A-Za-z0-9]+)'     device_replacement: 'Huawei Vodafone $1' @@ -723,10 +950,16 @@   ##########   - regex: 'Lenovo\-([A-Za-z0-9]+)'     device_replacement: 'Lenovo $1'-  - regex: 'Lenovo\_([A-Za-z0-9]+)'+  - regex: 'Lenovo_([A-Za-z0-9]+)'     device_replacement: 'Lenovo $1'    ##########+  # HbbTV (European and Australian standard)+  # written before the LG regexes, as LG is making HbbTV too+  ##########+  - regex: '(HbbTV)/[0-9]+\.[0-9]+\.[0-9]+'++  ##########   # lg   ##########   - regex: 'LG/([A-Za-z0-9]+)'@@ -764,8 +997,22 @@     device_replacement: 'Motorola $1'   - regex: 'MOT\-([A-Za-z0-9]+)'     device_replacement: 'Motorola $1'+    +  ##########+  # nintendo+  ##########+  - regex: '(Nintendo WiiU)'+    device_replacement: 'Nintendo Wii U'+  - regex: 'Nintendo (DS|3DS|DSi|Wii);'+    device_replacement: 'Nintendo $1'    ##########+  # pantech+  ##########+  - regex: 'Pantech([A-Za-z0-9]+)'+    device_replacement: 'Pantech $1'+ +  ##########   # philips   ##########   - regex: 'Philips([A-Za-z0-9]+)'@@ -782,6 +1029,12 @@     device_replacement: 'Samsung $1'    ##########+  # Sega+  ##########+  - regex: 'Dreamcast'+    device_replacement: 'Sega Dreamcast'++  ##########   # Softbank   ##########   - regex: 'Softbank/1\.0/([A-Za-z0-9]+)'@@ -790,10 +1043,9 @@     device_replacement: 'Softbank $1'    ##########-  # Sony+  # WebTV   ##########-  - regex: '(PlayStation3 PPC)'-    device_replacement: 'Playstation 3'+  - regex: '(WebTV)/(\d+).(\d+)'    ##########   # Generic Smart Phone@@ -804,58 +1056,18 @@   ##########   # Generic Feature Phone   ##########-  - regex: '^(1207|3gso|4thp|501i|502i|503i|504i|505i|506i|6310|6590|770s|802s|a wa|acer|acs\-|airn|alav|asus|attw|au\-m|aur |aus |abac|acoo|aiko|alco|alca|amoi|anex|anny|anyw|aptu|arch|argo|bell|bird|bw\-n|bw\-u|beck|benq|bilb|blac|c55/|cdm\-|chtm|capi|comp|cond|craw|dall|dbte|dc\-s|dica|ds\-d|ds12|dait|devi|dmob|doco|dopo|el49|erk0|esl8|ez40|ez60|ez70|ezos|ezze|elai|emul|eric|ezwa|fake|fly\-|fly\_|g\-mo|g1 u|g560|gf\-5|grun|gene|go.w|good|grad|hcit|hd\-m|hd\-p|hd\-t|hei\-|hp i|hpip|hs\-c|htc |htc\-|htca|htcg)'+  - regex: '^(1207|3gso|4thp|501i|502i|503i|504i|505i|506i|6310|6590|770s|802s|a wa|acer|acs\-|airn|alav|asus|attw|au\-m|aur |aus |abac|acoo|aiko|alco|alca|amoi|anex|anny|anyw|aptu|arch|argo|bell|bird|bw\-n|bw\-u|beck|benq|bilb|blac|c55/|cdm\-|chtm|capi|comp|cond|craw|dall|dbte|dc\-s|dica|ds\-d|ds12|dait|devi|dmob|doco|dopo|el49|erk0|esl8|ez40|ez60|ez70|ezos|ezze|elai|emul|eric|ezwa|fake|fly\-|fly_|g\-mo|g1 u|g560|gf\-5|grun|gene|go.w|good|grad|hcit|hd\-m|hd\-p|hd\-t|hei\-|hp i|hpip|hs\-c|htc |htc\-|htca|htcg)'     device_replacement: 'Generic Feature Phone'-  - regex: '^(htcp|htcs|htct|htc\_|haie|hita|huaw|hutc|i\-20|i\-go|i\-ma|i230|iac|iac\-|iac/|ig01|im1k|inno|iris|jata|java|kddi|kgt|kgt/|kpt |kwc\-|klon|lexi|lg g|lg\-a|lg\-b|lg\-c|lg\-d|lg\-f|lg\-g|lg\-k|lg\-l|lg\-m|lg\-o|lg\-p|lg\-s|lg\-t|lg\-u|lg\-w|lg/k|lg/l|lg/u|lg50|lg54|lge\-|lge/|lynx|leno|m1\-w|m3ga|m50/|maui|mc01|mc21|mcca|medi|meri|mio8|mioa|mo01|mo02|mode|modo|mot |mot\-|mt50|mtp1|mtv |mate|maxo|merc|mits|mobi|motv|mozz|n100|n101|n102|n202|n203|n300|n302|n500|n502|n505|n700|n701|n710|nec\-|nem\-|newg|neon)'+  - regex: '^(htcp|htcs|htct|htc_|haie|hita|huaw|hutc|i\-20|i\-go|i\-ma|i230|iac|iac\-|iac/|ig01|im1k|inno|iris|jata|java|kddi|kgt|kgt/|kpt |kwc\-|klon|lexi|lg g|lg\-a|lg\-b|lg\-c|lg\-d|lg\-f|lg\-g|lg\-k|lg\-l|lg\-m|lg\-o|lg\-p|lg\-s|lg\-t|lg\-u|lg\-w|lg/k|lg/l|lg/u|lg50|lg54|lge\-|lge/|lynx|leno|m1\-w|m3ga|m50/|maui|mc01|mc21|mcca|medi|meri|mio8|mioa|mo01|mo02|mode|modo|mot |mot\-|mt50|mtp1|mtv |mate|maxo|merc|mits|mobi|motv|mozz|n100|n101|n102|n202|n203|n300|n302|n500|n502|n505|n700|n701|n710|nec\-|nem\-|newg|neon)'     device_replacement: 'Generic Feature Phone'   - regex: '^(netf|noki|nzph|o2 x|o2\-x|opwv|owg1|opti|oran|ot\-s|p800|pand|pg\-1|pg\-2|pg\-3|pg\-6|pg\-8|pg\-c|pg13|phil|pn\-2|pt\-g|palm|pana|pire|pock|pose|psio|qa\-a|qc\-2|qc\-3|qc\-5|qc\-7|qc07|qc12|qc21|qc32|qc60|qci\-|qwap|qtek|r380|r600|raks|rim9|rove|s55/|sage|sams|sc01|sch\-|scp\-|sdk/|se47|sec\-|sec0|sec1|semc|sgh\-|shar|sie\-|sk\-0|sl45|slid|smb3|smt5|sp01|sph\-|spv |spv\-|sy01|samm|sany|sava|scoo|send|siem|smar|smit|soft|sony|t\-mo|t218|t250|t600|t610|t618|tcl\-|tdg\-|telm|tim\-|ts70|tsm\-|tsm3|tsm5|tx\-9|tagt)'     device_replacement: 'Generic Feature Phone'-  - regex: '^(talk|teli|topl|tosh|up.b|upg1|utst|v400|v750|veri|vk\-v|vk40|vk50|vk52|vk53|vm40|vx98|virg|vite|voda|vulc|w3c |w3c\-|wapj|wapp|wapu|wapm|wig |wapi|wapr|wapv|wapy|wapa|waps|wapt|winc|winw|wonu|x700|xda2|xdag|yas\-|your|zte\-|zeto|aste|audi|avan|blaz|brew|brvw|bumb|ccwa|cell|cldc|cmd\-|dang|eml2|fetc|hipt|http|ibro|idea|ikom|ipaq|jbro|jemu|jigs|keji|kyoc|kyok|libw|m\-cr|midp|mmef|moto|mwbp|mywa|newt|nok6|o2im|pant|pdxg|play|pluc|port|prox|rozo|sama|seri|smal|symb|treo|upsi|vx52|vx53|vx60|vx61|vx70|vx80|vx81|vx83|vx85|wap\-|webc|whit|wmlb|xda\-|xda\_)'+  - regex: '^(talk|teli|topl|tosh|up.b|upg1|utst|v400|v750|veri|vk\-v|vk40|vk50|vk52|vk53|vm40|vx98|virg|vite|voda|vulc|w3c |w3c\-|wapj|wapp|wapu|wapm|wig |wapi|wapr|wapv|wapy|wapa|waps|wapt|winc|winw|wonu|x700|xda2|xdag|yas\-|your|zte\-|zeto|aste|audi|avan|blaz|brew|brvw|bumb|ccwa|cell|cldc|cmd\-|dang|eml2|fetc|hipt|http|ibro|idea|ikom|ipaq|jbro|jemu|jigs|keji|kyoc|kyok|libw|m\-cr|midp|mmef|moto|mwbp|mywa|newt|nok6|o2im|pant|pdxg|play|pluc|port|prox|rozo|sama|seri|smal|symb|treo|upsi|vx52|vx53|vx60|vx61|vx70|vx80|vx81|vx83|vx85|wap\-|webc|whit|wmlb|xda\-|xda_)'     device_replacement: 'Generic Feature Phone'    ##########   # Spiders (this is hack...)   ##########-  - regex: '(bot|borg|google(^tv)|yahoo|slurp|msnbot|msrbot|openbot|archiver|netresearch|lycos|scooter|altavista|teoma|gigabot|baiduspider|blitzbot|oegp|charlotte|furlbot|http%20client|polybot|htdig|ichiro|mogimogi|larbin|pompos|scrubby|searchsight|seekbot|semanticdiscovery|silk|snappy|speedy|spider|voila|vortex|voyager|zao|zeal|fast\-webcrawler|converacrawler|dataparksearch|findlinks)'+  - regex: '(bot|borg|google(^tv)|yahoo|slurp|msnbot|msrbot|openbot|archiver|netresearch|lycos|scooter|altavista|teoma|gigabot|baiduspider|blitzbot|oegp|charlotte|furlbot|http%20client|polybot|htdig|ichiro|mogimogi|larbin|pompos|scrubby|searchsight|seekbot|semanticdiscovery|silk|snappy|speedy|spider|voila|vortex|voyager|zao|zeal|fast\-webcrawler|converacrawler|dataparksearch|findlinks|crawler)'     device_replacement: 'Spider' --mobile_user_agent_families:-  - 'Firefox Mobile'-  - 'Opera Mobile'-  - 'Opera Mini'-  - 'Mobile Safari'-  - 'webOS'-  - 'IE Mobile'-  - 'Playstation Portable'-  - 'Nokia'-  - 'Blackberry'-  - 'Palm'-  - 'Silk'-  - 'Android'-  - 'Maemo'-  - 'Obigo'-  - 'Netfront'-  - 'AvantGo'-  - 'Teleca'-  - 'SEMC-Browser'-  - 'Bolt'-  - 'Iris'-  - 'UP.Browser'-  - 'Symphony'-  - 'Minimo'-  - 'Bunjaloo'-  - 'Jasmine'-  - 'Dolfin'-  - 'Polaris'-  - 'BREW'-  - 'Chrome Mobile'-  - 'UC Browser'-  - 'Tizen Browser'--# Some select mobile OSs report a desktop browser.-# make sure we note they're mobile-mobile_os_families:-  - 'Windows Phone 6.5'-  - 'Windows CE'-  - 'Symbian OS'
src/Web/UAParser/Core.hs view
@@ -84,10 +84,10 @@ -- | Results datatype for the parsed User-Agent data UAResult = UAResult {       uarFamily :: Text-    , uarV1 :: Maybe Text-    , uarV2 :: Maybe Text-    , uarV3 :: Maybe Text-    } deriving (Show, Eq, Typeable, Data)+    , uarV1     :: Maybe Text+    , uarV2     :: Maybe Text+    , uarV3     :: Maybe Text+    } deriving (Show, Read, Eq, Typeable, Data)   -------------------------------------------------------------------------------@@ -137,11 +137,11 @@ -- | Result type for 'parseOS' data OSResult = OSResult {       osrFamily :: Text-    , osrV1 :: Maybe Text-    , osrV2 :: Maybe Text-    , osrV3 :: Maybe Text-    , osrV4 :: Maybe Text-    } deriving (Show,Eq,Typeable,Data)+    , osrV1     :: Maybe Text+    , osrV2     :: Maybe Text+    , osrV3     :: Maybe Text+    , osrV4     :: Maybe Text+    } deriving (Show,Read,Eq,Typeable,Data)  instance Default OSResult where     def = OSResult "" Nothing Nothing Nothing Nothing@@ -171,32 +171,32 @@ ------------------------------------------------------------------------------- -- | User-Agent string parser data data UAConfig = UAConfig {-      uaParsers :: [UAParser]-    , osParsers :: [OSParser]+      uaParsers  :: [UAParser]+    , osParsers  :: [OSParser]     , devParsers :: [DevParser]     } deriving (Eq,Show)   ------------------------------------------------------------------------------- data UAParser = UAParser {-      uaRegex :: Regex+      uaRegex  :: Regex     , uaFamRep :: Maybe Text-    , uaV1Rep :: Maybe Text+    , uaV1Rep  :: Maybe Text     } deriving (Eq,Show)   ------------------------------------------------------------------------------- data OSParser = OSParser {-      osRegex :: Regex+      osRegex  :: Regex     , osFamRep :: Maybe Text-    , osRep1 :: Maybe Text-    , osRep2 :: Maybe Text+    , osRep1   :: Maybe Text+    , osRep2   :: Maybe Text     } deriving (Eq,Show)   data DevParser = DevParser {       devRegex :: Regex-    , devRep :: Maybe Text+    , devRep   :: Maybe Text     } deriving (Eq,Show)  
+ test/src/TestSuite.hs view
@@ -0,0 +1,200 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE TemplateHaskell   #-}++module Main where+++-------------------------------------------------------------------------------+import           Control.Applicative+import           Control.DeepSeq+import           Control.Monad+import           Criterion.Main                 as C+import           Data.Aeson+import           Data.ByteString.Char8          (ByteString)+import           Data.DeriveTH+import           Data.Text                      (Text)+import qualified Data.Text                      as T+import qualified Data.Text.Encoding             as T+import           Data.Yaml+import           System.Environment+import           System.FilePath.Posix+import           Test.Framework                 (defaultMain, testGroup)+import           Test.Framework                 as T+import           Test.Framework.Providers.HUnit as T+import           Test.HUnit                     hiding (Test, path)+import           Text.Regex.PCRE.Light+-------------------------------------------------------------------------------+import           Web.UAParser.Core+-------------------------------------------------------------------------------+++-------------------------------------------------------------------------------+main = do+  arg <- getArgs+  case arg of+    ["bench"] -> benchMain+    _ -> testMain++++                              ------------------+                              -- Benchmarking --+                              ------------------+++-------------------------------------------------------------------------------+benchMain = do+  conf <- loadConfig "../regexes.yaml"+  cases <- loadTests "test_user_agent_parser.yaml"+  cases2 <- loadTests "firefox_user_agent_strings.yaml"+  let allC = cases ++ cases2+      ua = bench "UA Parsing" $ nf (map (parseUA conf . uatcString)) allC+  print $ show (length allC) ++ " strings being parsed."+  C.defaultMain [ua]++++                                 -------------+                                 -- Testing --+                                 -------------+++testMain = T.defaultMain tests+    where+      tests =+        [ uaTests+        , osTests ]+++                               ----------------+                               -- UA Testing --+                               ----------------+++-------------------------------------------------------------------------------+uaTests = buildTest $ do+  conf <- loadConfig "../regexes.yaml"+  cases <- loadTests "test_user_agent_parser.yaml"+  cases2 <- loadTests "firefox_user_agent_strings.yaml"+  let allC = cases ++ cases2+  return $ testGroup "UA Parsing Tests" $ map (testUAParser conf) allC++++-------------------------------------------------------------------------------+testUAParser :: UAConfig -> UserAgentTestCase -> Test+testUAParser config UATC{..} = testCase tn $ do+  case parsed of+    Nothing -> assertFailure "Can't produce UAResult"+    Just UAResult{..} -> do+     assertEqual "family is same" uatcFamily uarFamily+     -- assertEqual "v1 is the same" uatcV1 uarV1+     -- assertEqual "v2 is the same" uatcV2 uarV2+     -- assertEqual "v3 is the same" uatcV3 uarV3+  where+    parsed = parseUA config uatcString+    tn = T.unpack $ T.intercalate "/" ["UA Test: ", uatcFamily, m uatcV1, m uatcV2, m uatcV3]+    m x = maybe "-" id x+++                               ----------------+                               -- OS Testing --+                               ----------------++++-------------------------------------------------------------------------------+osTests = buildTest $ do+  conf <- loadConfig "../regexes.yaml"+  cases <- loadTests "test_user_agent_parser_os.yaml"+  return $ testGroup "OS Parsing Tests" $ map (testOSParser conf) cases++++-------------------------------------------------------------------------------+testOSParser :: UAConfig -> OSTestCase -> Test+testOSParser config OSTC{..} = testCase tn $ do+  case parsed of+    Nothing -> assertFailure "Can't produce OSResult"+    Just OSResult{..} -> do+     assertEqual "family is same" ostcFamily osrFamily+     assertEqual "major is the same" ostcV1 osrV1+     assertEqual "minor is the same" ostcV2  osrV2+     assertEqual "patch is the same" ostcV3  osrV3+     assertEqual "patch_minor is the same" ostcV4  osrV4+  where+    parsed = parseOS config ostcString+    tn = T.unpack $ T.intercalate "/"+         ["OS Test: ", ostcFamily, m ostcV1, m ostcV2, m ostcV3, m ostcV4]+    m x = maybe "-" id x+++++                           ------------------------+                           -- Loading Test Cases --+                           ------------------------++++-------------------------------------------------------------------------------+loadTests :: FromJSON a => FilePath -> IO a+loadTests fp = parseMonad p =<< either error id `fmap` decodeFile' fp'+  where+    fp' = "../test_resources" </> fp+    p (Object x) = x .: "test_cases"+++-------------------------------------------------------------------------------+data UserAgentTestCase = UATC {+      uatcString :: ByteString+    , uatcFamily :: Text+    , uatcV1     :: Maybe Text+    , uatcV2     :: Maybe Text+    , uatcV3     :: Maybe Text+    } deriving (Show)+++-------------------------------------------------------------------------------+instance FromJSON UserAgentTestCase where+    parseJSON (Object v) =+      UATC <$> v .: "user_agent_string"+           <*> (v .: "family" <|> return "")+           <*> (v .:? "v1" <|> return Nothing)+           <*> (v .:? "v2" <|> return Nothing)+           <*> (v .:? "v3" <|> return Nothing)+++-------------------------------------------------------------------------------+data OSTestCase = OSTC {+      ostcString :: ByteString+    , ostcFamily :: Text+    , ostcV1     :: Maybe Text+    , ostcV2     :: Maybe Text+    , ostcV3     :: Maybe Text+    , ostcV4     :: Maybe Text+    } deriving (Show)+++-------------------------------------------------------------------------------+instance FromJSON OSTestCase where+    parseJSON (Object v) =+      OSTC <$> (v .: "user_agent_string" <|> return "")+           <*> (v .: "family" <|> return "")+           <*> nonBlank (v .:? "major" <|> return Nothing)+           <*> nonBlank (v .:? "minor" <|> return Nothing)+           <*> nonBlank (v .:? "patch" <|> return Nothing)+           <*> nonBlank (v .:? "patch_minor" <|> return Nothing)++-------------------------------------------------------------------------------+nonBlank f = do+  res <- f+  return $ case res of+    Just "" -> Nothing+    Just x -> Just x+    Nothing -> Nothing++++$(derives [makeNFData] [''UAResult])
ua-parser.cabal view
@@ -1,6 +1,6 @@ Name:                ua-parser Description:         Please refer to the git/github README on the project for example usage.-Version:             0.2.1+Version:             0.3 Synopsis:            Port of ua-parser in Haskell License:             BSD3 License-file:        LICENSE@@ -8,7 +8,7 @@ Maintainer:          ozgun.ataman@soostone.com Category:            Web Build-type:          Simple-Cabal-version:       >=1.6+Cabal-version:       >=1.8  data-files: ./*.yaml @@ -38,3 +38,30 @@     , syb    ghc-options: -O2 -Wall  +++test-suite test+  type: exitcode-stdio-1.0+  main-is: TestSuite.hs+  hs-source-dirs: src test/src+  ghc-options: -O2 -Wall -fwarn-tabs++  Build-depends:       +      base+    , bytestring+    , text+    , criterion+    , deepseq+    , derive+    , HUnit+    , pcre-light+    , yaml >= 0.7+    , aeson+    , test-framework+    , test-framework-hunit+    , test-framework-quickcheck2+    , data-default+    , syb+    , filepath++