Skip to content

Commit

Permalink
Added support for deviceType detection inluding tests
Browse files Browse the repository at this point in the history
  • Loading branch information
flxReinoud committed Nov 18, 2014
1 parent 7d1c47b commit 706c211
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 85 deletions.
52 changes: 23 additions & 29 deletions device.go
@@ -1,36 +1,30 @@
package uaparser
var (
iPad = &itemSpec{
name: "iPad",
mustContains: []string{"iPad"},
}

iPhone = &itemSpec{
name: "iPhone",
mustContains: []string{"iPhone"},
}
var (
iPad = &itemSpec{
name: "iPad",
mustContains: []string{"iPad"},
}

iPod = &itemSpec{
name: "iPod",
mustContains: []string{"iPod"},
}
iPhone = &itemSpec{
name: "iPhone",
mustContains: []string{"iPhone"},
}

mac = &itemSpec{
name: "Macintosh",
mustContains: []string{"Macintosh"},
}
iPod = &itemSpec{
name: "iPod",
mustContains: []string{"iPod"},
}

pc = &itemSpec{
name: "PC",
mustContains: []string{"Windows", "Linux"},
mustNotContains: []string{"Windows Phone", "Android"},
}
mac = &itemSpec{
name: "Macintosh",
mustContains: []string{"Macintosh"},
}

_DEVICES = []*itemSpec {
iPad,
iPhone,
iPod,
mac,
pc,
}
_DEVICES = []*itemSpec{
iPad,
iPhone,
iPod,
mac,
}
)
59 changes: 59 additions & 0 deletions deviceType.go
@@ -0,0 +1,59 @@
package uaparser

var (
desktop = &itemSpec{
name: "Desktop",
mustContains: []string{"Windows", "Linux"},
mustNotContains: []string{"Windows Phone", "Android", "ARM"},
}

tablet = &itemSpec{
name: "Tablet",
mustContains: []string{"iPad", "Android", "Windows NT"},
mustNotContains: []string{
"Mobile ",
"C6802", // Xperia Z Android (which is a phone) does not include Mobile in UA-string so without this is seen as a tablet
},
}

phone = &itemSpec{
name: "Phone",
mustContains: []string{
"iPhone",
"Android",
"Windows Phone",
},
mustNotContains: []string{},
}

car = &itemSpec{
name: "Car",
mustContains: []string{
"QtCarBrowser",
},
mustNotContains: []string{},
}

smartTv = &itemSpec{
name: "SmartTV",
mustContains: []string{
"SMART-TV",
"AppleTV",
"CrKey",
"Large Screen",
"HbbTV",
"LG Browser",
"PhilipsTV",
"Opera TV",
},
mustNotContains: []string{},
}

_DEVICETYPES = []*itemSpec{
tablet,
phone,
car,
smartTv,
desktop,
}
)
11 changes: 11 additions & 0 deletions do_test.go
Expand Up @@ -6,9 +6,11 @@ import "fmt"
func TestParse(t *testing.T) {
var expectedBrowserNames map[string][]string = GetBrowserNames()
var expectedOperatingSystems map[string][]string = GetOSNames()
var expectedDeviceTypes map[string][]string = GetDeviceTypes()

_checkExepectations(t, expectedOperatingSystems, "os")
_checkExepectations(t, expectedBrowserNames, "browser")
_checkExepectations(t, expectedDeviceTypes, "deviceType")
}

func _checkExepectations(t *testing.T, expectations map[string][]string, testType string) {
Expand All @@ -25,6 +27,8 @@ func _checkExepectations(t *testing.T, expectations map[string][]string, testTyp
testResult, comparedTo = _checkBrowser(uaParseResult, expectation)
} else if testType == "os" {
testResult, comparedTo = _checkOs(uaParseResult, expectation)
} else if testType == "deviceType" {
testResult, comparedTo = _checkDeviceType(uaParseResult, expectation)
}

if !testResult {
Expand All @@ -47,3 +51,10 @@ func _checkOs(uainfo *UAInfo, expectation string) (result bool, comparedTo strin
}
return (uainfo.OS.Name == expectation), uainfo.OS.Name
}

func _checkDeviceType(uainfo *UAInfo, expectation string) (result bool, comparedTo string) {
if uainfo.DeviceType == nil {
return false, ""
}
return (uainfo.DeviceType.Name == expectation), uainfo.DeviceType.Name
}
8 changes: 4 additions & 4 deletions os.go
Expand Up @@ -9,7 +9,7 @@ var (

macOS = &itemSpec{
name: "Mac OS",
mustContains: []string{"Mac OS"},
mustContains: []string{"Mac OS", "Macintosh"},
mustNotContains: []string{"iPad", "iPhone", "iPod"},
versionSplitters: [][]string{
[]string{"Mac OS ", ";"},
Expand All @@ -29,7 +29,7 @@ var (
android = &itemSpec{
name: "Android",
mustContains: []string{"Android"},
mustNotContains: []string{},
mustNotContains: []string{"Windows Phone"},
versionSplitters: [][]string{
[]string{"Android ", ";"},
[]string{"Android-", " "},
Expand All @@ -38,8 +38,8 @@ var (

iOS = &itemSpec{
name: "iOS",
mustContains: []string{"CPU", "OS", "like Mac OS X"},
mustNotContains: []string{"Windows Phone OS"},
mustContains: []string{"CPU", "OS", "like Mac OS X", "iphone os"},
mustNotContains: []string{"Windows Phone"},
versionSplitters: [][]string{
[]string{"CPU iPhone OS ", " "},
[]string{"CPU OS ", " "},
Expand Down
108 changes: 56 additions & 52 deletions parser.go
@@ -1,84 +1,88 @@
package uaparser

import (
"strings"
"unicode"
"strings"
"unicode"
)

type itemSpec struct {
name string
mustContains []string
mustNotContains []string
versionSplitters [][]string
name string
mustContains []string
mustNotContains []string
versionSplitters [][]string
}

type InfoItem struct {
Name string
Version string
Name string
Version string
}

type UAInfo struct {
Browser,
Device,
OS *InfoItem
Browser,
Device,
DeviceType,
OS *InfoItem
}

func isEmptyString(str string) bool {
for _, char := range str {
if !unicode.IsSpace(char) {
return false
}
}
return true
for _, char := range str {
if !unicode.IsSpace(char) {
return false
}
}
return true
}

func contains(ua string, tokens []string) bool {
for _, tk := range tokens {
if strings.Contains(ua, tk) {
return true
}
}
return false
for _, tk := range tokens {
if strings.Contains(ua, tk) {
return true
}
}
return false
}

func matchSpec(ua string, spec *itemSpec) (info *InfoItem, ok bool) {
if !contains(ua, spec.mustContains) { return }
if contains(ua, spec.mustNotContains) { return }
if !contains(ua, spec.mustContains) {
return
}
if contains(ua, spec.mustNotContains) {
return
}

info = new(InfoItem)
info.Name = spec.name
ok = true
info = new(InfoItem)
info.Name = spec.name
ok = true

for _, splitter := range spec.versionSplitters {
if strings.Contains(ua, splitter[0]) {
if rmLeft := strings.Split(ua, splitter[0])[1];
strings.Contains(rmLeft, splitter[1]) || isEmptyString(splitter[1]) {
rmRight := strings.Split(rmLeft, splitter[1])[0]
info.Version = strings.TrimSpace(rmRight)
break
}
}
}
return
for _, splitter := range spec.versionSplitters {
if strings.Contains(ua, splitter[0]) {
if rmLeft := strings.Split(ua, splitter[0])[1]; strings.Contains(rmLeft, splitter[1]) || isEmptyString(splitter[1]) {
rmRight := strings.Split(rmLeft, splitter[1])[0]
info.Version = strings.TrimSpace(rmRight)
break
}
}
}
return
}

func searchIn(ua string, specs []*itemSpec) (info *InfoItem) {
for _, spec := range specs {
if result, ok := matchSpec(ua, spec); ok {
info = result
break
}
}
return
for _, spec := range specs {
if result, ok := matchSpec(ua, spec); ok {
info = result
break
}
}
return
}

func Parse(ua string) (info *UAInfo) {
info = new(UAInfo)
info = new(UAInfo)

info.Browser = searchIn(ua, _BROWSERS)
info.Device = searchIn(ua, _DEVICES)
info.OS = searchIn(ua, _OS)
info.Browser = searchIn(ua, _BROWSERS)
info.Device = searchIn(ua, _DEVICES)
info.DeviceType = searchIn(ua, _DEVICETYPES)
info.OS = searchIn(ua, _OS)

return
return
}

62 changes: 62 additions & 0 deletions testdata.go
Expand Up @@ -4798,3 +4798,65 @@ func GetOSNames() map[string][]string {

return expectedOperatingSystems
}

func GetDeviceTypes() map[string][]string {
var expectedDeviceTypes map[string][]string = make(map[string][]string)
expectedDeviceTypes["Tablet"] = []string{
"Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25",
"Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
"Mozilla/5.0 (Linux; U; Android 4.2.2; nl-nl; GT-P5210 Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 Mozilla/5.0 (Linux; U; Android 4.2.2; nl-nl; SM-T310 Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
"Mozilla/5.0 (Linux; Android 4.3; SGP311 Build/10.4.1.B.0.109) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.138 Safari/537.36",
"Mozilla/5.0 (Linux; Android 4.1.2; SGP321 Build/10.1.1.A.1.253) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19",
"Mozilla/5.0 (Linux; Android 4.4.4; SGP321 Build/10.5.1.A.0.283) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Safari/537.36 OPR/24.0.1565.82529",
"Mozilla/5.0 (Linux; u; Android 3.1; en-us; gt-p7510 build/hmj37) AppleWebkit/534.13 (KHTML, like Gecko) version/4.0 Safari/534.13",
"Mozilla/5.0 (iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10",
"Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; es-es) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B360 Safari/531.21.10",
"Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3",
"Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3",
"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)",
}
expectedDeviceTypes["Phone"] = []string{
"Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; Nexus One Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
"Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
"Mozilla/5.0 (Linux; Android 4.4.2; Nexus 5 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Mobile Safari/537.36",
"Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; Android 4.0.3; HTC One X Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; xx-xx) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7",
"Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; GT-I9500 Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; GT-I9295 Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.0; xx-xx; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.0; xx-xx; GT-I9305 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.1; xx-xx; LG-F240L Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.1; xx-xx; LG-E980/E98010g Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.1; xx-xx; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.1; xx-xx; SAMSUNG-SGH-I317 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 1020)",
"Mozilla/5.0 (Linux; U; Android 4.1; xx-xx; SonyEricssonC6603 Build/10.1.A.0.182) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; C6802 Build/14.1.B.0.461) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.0; xx-xx; MOT-XT925 Build/7.7.1Q-126_VQU_RL-9) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.0; xx-xx; MOT-XT923 Build/7.7.1Q-78_VQU_LE-24) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.1; xx-xx; XT926 Build/BWHD) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.0; xx-xx; HTC6435LVW Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (Linux; U; Android 4.1; xx-xx; HTC6435LVW 4G Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 625)",
"Mozilla/5.0 (Linux; U; Android 4.3; xx-xx; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
}
expectedDeviceTypes["Car"] = []string{
"Model S (3/13/13, v?.?) = Mozilla/5.0 (X11; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) QtCarBrowser Safari/533.3",
"Model S (4/8/14, v5.9) = Mozilla/5.0 (X11; Linux) AppleWebKit/534.34 (KHTML, like Gecko) QtCarBrowser Safari/534.34",
}
expectedDeviceTypes["SmartTV"] = []string{
"Mozilla/5.0 (SMART-TV; X11; Linux armv7l) AppleWebkit/537.42 (KHTML, like Gecko) Chromium/25.0.1349.2 Chrome/25.0.1349.2 Safari/537.42",
"iTunes-AppleTV/4.1",
"Mozilla/5.0 (CrKey armv7l 1.4.15250) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.0 Safari/537.36",
"Mozilla/5.0 (X11; U: Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.127 Large Screen Safari/533.4 GoogleTV/b39389",
"Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.1 (KHTML, like Gecko) HbbTV/1.1.1 (+PVR;Mstar;OWB;;;)",
"Mozilla/5.0 (DirectFB; U; Linux 35230; en) AppleWebKit/531.2+ (KHTML, like Gecko) Safari/531.2+ LG Browser/4.1.4(+3D+SCREEN+TUNER; LGE; 42LW5700-SA; 04.02.28; 0x00000001;); LG NetCast.TV-2011",
"Opera/9.80 (Linux mips; HbbTV/1.2.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/4.2.0 PHILIPSTV/1.1.1 Firmware/171.56.0 (PhilipsTV, 1.1.1,) en) Presto/2.12.362 Version/12.11",
"Mozilla/5.0 (SmartHub; SMART-TV; U; Linux/SmartTV; Maple2012) AppleWebKit/534.7 (KHTML, like Gecko) SmartTV Safari/534.7",
"Opera/9.80 (Linux armv6l; Opera TV Store/5599; (SonyBDP/BDV13)) Presto/2.12.362 Version/12.11",
}
return expectedDeviceTypes
}

0 comments on commit 706c211

Please sign in to comment.