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 +340/−128
- src/Web/UAParser/Core.hs +17/−17
- test/src/TestSuite.hs +200/−0
- ua-parser.cabal +29/−2
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++