# -*- coding: utf-8 -*-
# Copyright (C) 2011  Michał Masłowski  <mtjm@mtjm.eu>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


"""Tests for `hclient.machine`."""


import unittest


class KernelVersionTestCase(unittest.TestCase):

	"""Tests for obtaining kernel version."""

	def test_single_line(self):
		"""Check that kernel version takes single line"""
		from hclient.machine import KERNEL_VERSION
		self.assertFalse("\n" in KERNEL_VERSION)

	def test_major_version(self):
		"""Check that obtained kernel version looks like Linux version"""
		from hclient.machine import KERNEL_VERSION
		self.assertTrue(KERNEL_VERSION.startswith("2.6.")
						or KERNEL_VERSION.startswith("3.")
						or KERNEL_VERSION.startswith("4."))


#: ``/usr/lib/os-release`` from Parabola.
_PARABOLA_OS_RELEASE ="""NAME="Parabola"
ID=parabola
ID_LIKE=arch
PRETTY_NAME="Parabola GNU/Linux-libre"
ANSI_COLOR="1;35"
HOME_URL="https://parabolagnulinux.org/"
BUG_REPORT_URL="https://labs.parabola.nu/"

"""
#: The same parsed into a dictionary.
_PARABOLA_DATA = {'ANSI_COLOR': '1;35',
				  'BUG_REPORT_URL': 'https://labs.parabola.nu/',
				  'HOME_URL': 'https://parabolagnulinux.org/',
				  'ID': 'parabola',
				  'ID_LIKE': 'arch',
				  'NAME': 'Parabola',
				  'PRETTY_NAME': 'Parabola GNU/Linux-libre'}


class _ParabolaOpener(object):

	"""Get Parabola identification files."""

	@staticmethod
	def open(file_name):
		"""Open a file."""
		from cStringIO import StringIO
		from contextlib import closing
		if file_name == "/etc/arch-release":
			string = StringIO("\n")
		elif file_name == "/etc/lsb-release":
			string = StringIO("""DISTRIB_ID=parabola
DISTRIB_RELEASE=rolling
DISTRIB_DESCRIPTION="Parabola GNU/Linux-libre"
""")
		elif file_name == "/usr/lib/os-release":
			string = StringIO(_PARABOLA_OS_RELEASE)
		elif file_name == "/etc/parabola-release":
			string = StringIO("Parabola GNU/Linux-libre release\n")
		else:
			raise IOError()
		return closing(string)

	@staticmethod
	def glob(pattern):
		"""List files."""
		if pattern != "/etc/*-release":
			raise NotImplemented("Unknown pattern %r" % pattern)
		return ["/etc/arch-release", "/etc/lsb-release",
				"/usr/lib/os-release", "/etc/parabola-release"]


class _TaranisOpener(object):

	"""Get Trisquel Taranis identification files."""

	@staticmethod
	def open(file_name):
		"""Open a file."""
		from cStringIO import StringIO
		from contextlib import closing
		if file_name == "/etc/lsb-release":
			string = StringIO("""DISTRIB_ID=Trisquel
DISTRIB_RELEASE=4.1
DISTRIB_CODENAME=taranis
DISTRIB_DESCRIPTION="Trisquel 4.1 LTS"
""")
		else:
			raise IOError()
		return closing(string)

	@staticmethod
	def glob(pattern):
		"""List files."""
		if pattern != "/etc/*-release":
			raise NotImplemented("Unknown pattern %r" % pattern)
		return ["/etc/lsb-release"]


class _QuantalOpener(object):

	"""Get Ubuntu Quantal identification files."""

	@staticmethod
	def open(file_name):
		"""Open a file."""
		from cStringIO import StringIO
		from contextlib import closing
		if file_name == "/etc/os-release":
			string = StringIO("""NAME="Ubuntu"
VERSION="12.10, Quantal Quetzal"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu quantal (12.10)"
VERSION_ID="12.10"
""")
		elif file_name == "/etc/lsb-release":
			string = StringIO("""DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=12.10
DISTRIB_CODENAME=quantal
DISTRIB_DESCRIPTION="Ubuntu 12.10"
""")
		else:
			raise IOError()
		return closing(string)

	@staticmethod
	def glob(pattern):
		"""List files."""
		if pattern != "/etc/*-release":
			raise NotImplemented("Unknown pattern %r" % pattern)
		return ["/etc/os-release", "/etc/lsb-release"]


class _EmptyOpener(object):

	"""Get no identification files."""

	@staticmethod
	def open(file_name):
		"""Open a file."""
		raise IOError()

	@staticmethod
	def glob(pattern):
		"""List files."""
		return []


class DistroNameTestCase(unittest.TestCase):

	"""Tests for obtaining distro name."""

	def test_nonempty(self):
		"""Check that distro name is nonempty"""
		from hclient.machine import user_distribution
		self.assertTrue(user_distribution())

	def test_parse_os_release(self):
		"""Check that Parabola /usr/lib/os-release is correctly parsed."""
		from hclient.machine import parse_os_release
		from StringIO import StringIO
		self.assertEquals(_PARABOLA_DATA,
						  parse_os_release(StringIO(_PARABOLA_OS_RELEASE)))

	def test_distro_from_os_release(self):
		"""Check that Parabola is detected using /usr/lib/os-release."""
		from hclient.machine import distro_from_os_release
		self.assertEquals("parabola", distro_from_os_release(_PARABOLA_DATA))

	def test_user_distribution_parabola(self):
		"""Check that Parabola is detected."""
		from hclient.machine import user_distribution
		self.assertEquals("parabola", user_distribution(_ParabolaOpener))

	def test_user_distribution_taranis(self):
		"""Check that Trisquel Taranis is detected."""
		from hclient.machine import user_distribution
		self.assertEquals("trisquel_4_0", user_distribution(_TaranisOpener))

	def test_user_distribution_quantal(self):
		"""Check that Ubuntu Quantal is not detected."""
		from hclient.machine import user_distribution
		self.assertEquals("", user_distribution(_QuantalOpener))

	def test_user_distribution_unknown(self):
		"""Check that unknown distro is not detected."""
		from hclient.machine import user_distribution
		self.assertEquals("", user_distribution(_EmptyOpener))

#: Output of ``lspci -vmmnnk`` for testing.
_LSPCI_OUTPUT = """Slot:	00:00.0
Class:	RAM memory [0500]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 Memory Controller [0547]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a2

Slot:	00:01.0
Class:	ISA bridge [0601]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 ISA Bridge [0548]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a2

Slot:	00:01.1
Class:	SMBus [0c05]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 SMBus [0542]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a2
Driver:	nForce2_smbus
Module:	i2c-nforce2

Slot:	00:02.0
Class:	USB Controller [0c03]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 OHCI USB 1.1 Controller [055e]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a2
ProgIf:	10
Driver:	ohci_hcd
Module:	ohci-hcd

Slot:	00:02.1
Class:	USB Controller [0c03]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 EHCI USB 2.0 Controller [055f]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a2
ProgIf:	20
Driver:	ehci_hcd
Module:	ehci-hcd

Slot:	00:04.0
Class:	USB Controller [0c03]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 OHCI USB 1.1 Controller [055e]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a2
ProgIf:	10
Driver:	ohci_hcd
Module:	ohci-hcd

Slot:	00:04.1
Class:	USB Controller [0c03]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 EHCI USB 2.0 Controller [055f]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a2
ProgIf:	20
Driver:	ehci_hcd
Module:	ehci-hcd

Slot:	00:06.0
Class:	IDE interface [0101]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 IDE Controller [0560]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a1
ProgIf:	8a
Driver:	pata_amd
Module:	pata_amd
Module:	ata_generic
Module:	pata_acpi
Module:	amd74xx
Module:	ide-pci-generic

Slot:	00:07.0
Class:	Audio device [0403]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 High Definition Audio [055c]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [8345]
Rev:	a1
Driver:	HDA Intel
Module:	snd-hda-intel

Slot:	00:08.0
Class:	PCI bridge [0604]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 PCI Bridge [0561]
Rev:	a2
ProgIf:	01

Slot:	00:09.0
Class:	SATA controller [0106]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 AHCI Controller [0554]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a2
ProgIf:	01
Driver:	ahci
Module:	ahci

Slot:	00:0a.0
Class:	Ethernet controller [0200]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 Ethernet [054c]
SVendor:	ASUSTeK Computer Inc. [1043]
SDevice:	Device [82b3]
Rev:	a2
Driver:	forcedeth
Module:	forcedeth

Slot:	00:0b.0
Class:	PCI bridge [0604]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 PCI Express Bridge [0562]
Rev:	a2
Driver:	pcieport
Module:	shpchp

Slot:	00:0c.0
Class:	PCI bridge [0604]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 PCI Express Bridge [0563]
Rev:	a2
Driver:	pcieport
Module:	shpchp

Slot:	00:0d.0
Class:	PCI bridge [0604]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 PCI Express Bridge [0563]
Rev:	a2
Driver:	pcieport
Module:	shpchp

Slot:	00:0e.0
Class:	PCI bridge [0604]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 PCI Express Bridge [0563]
Rev:	a2
Driver:	pcieport
Module:	shpchp

Slot:	00:0f.0
Class:	PCI bridge [0604]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 PCI Express Bridge [0563]
Rev:	a2
Driver:	pcieport
Module:	shpchp

Slot:	00:10.0
Class:	PCI bridge [0604]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 PCI Express Bridge [0563]
Rev:	a2
Driver:	pcieport
Module:	shpchp

Slot:	00:11.0
Class:	PCI bridge [0604]
Vendor:	nVidia Corporation [10de]
Device:	MCP67 PCI Express Bridge [0563]
Rev:	a2
Driver:	pcieport
Module:	shpchp

Slot:	00:18.0
Class:	Host bridge [0600]
Vendor:	Advanced Micro Devices [AMD] [1022]
Device:	Family 10h Processor HyperTransport Configuration [1200]

Slot:	00:18.1
Class:	Host bridge [0600]
Vendor:	Advanced Micro Devices [AMD] [1022]
Device:	Family 10h Processor Address Map [1201]

Slot:	00:18.2
Class:	Host bridge [0600]
Vendor:	Advanced Micro Devices [AMD] [1022]
Device:	Family 10h Processor DRAM Controller [1202]
Module:	amd64_edac_mod

Slot:	00:18.3
Class:	Host bridge [0600]
Vendor:	Advanced Micro Devices [AMD] [1022]
Device:	Family 10h Processor Miscellaneous Control [1203]
Module:	k10temp

Slot:	00:18.4
Class:	Host bridge [0600]
Vendor:	Advanced Micro Devices [AMD] [1022]
Device:	Family 10h Processor Link Control [1204]

Slot:	01:09.0
Class:	Network controller [0280]
Vendor:	Atheros Communications Inc. [168c]
Device:	AR922X Wireless Network Adapter [0029]
SVendor:	Atheros Communications Inc. [168c]
SDevice:	Device [2091]
Rev:	01
Driver:	ath9k
Module:	ath9k

Slot:	02:00.0
Class:	VGA compatible controller [0300]
Vendor:	nVidia Corporation [10de]
Device:	G71 [GeForce 7300 GS] [01df]
Rev:	a1
Driver:	nouveau
Module:	nouveau
Module:	nvidiafb

"""


_PARSED_LSPCI_OUTPUT = (
	{"Slot": "00:00.0",
	 "Class": ("RAM memory", 0x500),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 Memory Controller", 0x0547),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a2"},
	{"Slot": "00:01.0",
	 "Class": ("ISA bridge", 0x0601),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 ISA Bridge", 0x0548),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a2"},
	{"Slot": "00:01.1",
	 "Class": ("SMBus", 0x0c05),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 SMBus", 0x0542),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a2",
	 "Driver": "nForce2_smbus",
	 "Module": ["i2c-nforce2"]},
	{"Slot": "00:02.0",
	 "Class": ("USB Controller", 0x0c03),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 OHCI USB 1.1 Controller", 0x055e),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a2",
	 "ProgIf": "10",
	 "Driver": "ohci_hcd",
	 "Module": ["ohci-hcd"]},
	{"Slot": "00:02.1",
	 "Class": ("USB Controller", 0x0c03),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 EHCI USB 2.0 Controller", 0x055f),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a2",
	 "ProgIf": "20",
	 "Driver": "ehci_hcd",
	 "Module": ["ehci-hcd"]},
	{"Slot": "00:04.0",
	 "Class": ("USB Controller", 0x0c03),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 OHCI USB 1.1 Controller", 0x055e),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a2",
	 "ProgIf": "10",
	 "Driver": "ohci_hcd",
	 "Module": ["ohci-hcd"]},
	{"Slot": "00:04.1",
	 "Class": ("USB Controller", 0x0c03),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 EHCI USB 2.0 Controller", 0x055f),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a2",
	 "ProgIf": "20",
	 "Driver": "ehci_hcd",
	 "Module": ["ehci-hcd"]},
	{"Slot": "00:06.0",
	 "Class": ("IDE interface", 0x0101),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 IDE Controller", 0x0560),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a1",
	 "ProgIf": "8a",
	 "Driver": "pata_amd",
	 "Module": ["pata_amd", "ata_generic", "pata_acpi", "amd74xx",
				"ide-pci-generic"]},
	{"Slot": "00:07.0",
	 "Class": ("Audio device", 0x0403),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 High Definition Audio", 0x055c),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x8345),
	 "Rev": "a1",
	 "Driver": "HDA Intel",
	 "Module": ["snd-hda-intel"]},
	{"Slot": "00:08.0",
	 "Class": ("PCI bridge", 0x0604),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 PCI Bridge", 0x0561),
	 "Rev": "a2",
	 "ProgIf": "01"},
	{"Slot": "00:09.0",
	 "Class": ("SATA controller", 0x0106),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 AHCI Controller", 0x0554),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a2",
	 "ProgIf": "01",
	 "Driver": "ahci",
	 "Module": ["ahci"]},
	{"Slot": "00:0a.0",
	 "Class": ("Ethernet controller", 0x0200),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 Ethernet", 0x054c),
	 "SVendor": ("ASUSTeK Computer Inc.", 0x1043),
	 "SDevice": ("Device", 0x82b3),
	 "Rev": "a2",
	 "Driver": "forcedeth",
	 "Module": ["forcedeth"]},
	{"Slot": "00:0b.0",
	 "Class": ("PCI bridge", 0x0604),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 PCI Express Bridge", 0x0562),
	 "Rev": "a2",
	 "Driver": "pcieport",
	 "Module": ["shpchp"]},
	{"Slot": "00:0c.0",
	 "Class": ("PCI bridge", 0x0604),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 PCI Express Bridge", 0x0563),
	 "Rev": "a2",
	 "Driver": "pcieport",
	 "Module": ["shpchp"]},
	{"Slot": "00:0d.0",
	 "Class": ("PCI bridge", 0x0604),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 PCI Express Bridge", 0x0563),
	 "Rev": "a2",
	 "Driver": "pcieport",
	 "Module": ["shpchp"]},
	{"Slot": "00:0e.0",
	 "Class": ("PCI bridge", 0x0604),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 PCI Express Bridge", 0x0563),
	 "Rev": "a2",
	 "Driver": "pcieport",
	 "Module": ["shpchp"]},
	{"Slot": "00:0f.0",
	 "Class": ("PCI bridge", 0x0604),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 PCI Express Bridge", 0x0563),
	 "Rev": "a2",
	 "Driver": "pcieport",
	 "Module": ["shpchp"]},
	{"Slot": "00:10.0",
	 "Class": ("PCI bridge", 0x0604),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 PCI Express Bridge", 0x0563),
	 "Rev": "a2",
	 "Driver": "pcieport",
	 "Module": ["shpchp"]},
	{"Slot": "00:11.0",
	 "Class": ("PCI bridge", 0x0604),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("MCP67 PCI Express Bridge", 0x0563),
	 "Rev": "a2",
	 "Driver": "pcieport",
	 "Module": ["shpchp"]},
	{"Slot": "00:18.0",
	 "Class": ("Host bridge", 0x0600),
	 "Vendor": ("Advanced Micro Devices [AMD]", 0x1022),
	 "Device": ("Family 10h Processor HyperTransport Configuration", 0x1200)},
	{"Slot": "00:18.1",
	 "Class": ("Host bridge", 0x0600),
	 "Vendor": ("Advanced Micro Devices [AMD]", 0x1022),
	 "Device": ("Family 10h Processor Address Map", 0x1201)},
	{"Slot": "00:18.2",
	 "Class": ("Host bridge", 0x0600),
	 "Vendor": ("Advanced Micro Devices [AMD]", 0x1022),
	 "Device": ("Family 10h Processor DRAM Controller", 0x1202),
	 "Module": ["amd64_edac_mod"]},
	{"Slot": "00:18.3",
	 "Class": ("Host bridge", 0x0600),
	 "Vendor": ("Advanced Micro Devices [AMD]", 0x1022),
	 "Device": ("Family 10h Processor Miscellaneous Control", 0x1203),
	 "Module": ["k10temp"]},
	{"Slot": "00:18.4",
	 "Class": ("Host bridge", 0x0600),
	 "Vendor": ("Advanced Micro Devices [AMD]", 0x1022),
	 "Device": ("Family 10h Processor Link Control", 0x1204)},
	{"Slot": "01:09.0",
	 "Class": ("Network controller", 0x0280),
	 "Vendor": ("Atheros Communications Inc.", 0x168c),
	 "Device": ("AR922X Wireless Network Adapter", 0x0029),
	 "SVendor": ("Atheros Communications Inc.", 0x168c),
	 "SDevice": ("Device", 0x2091),
	 "Rev": "01",
	 "Driver": "ath9k",
	 "Module": ["ath9k"]},
	{"Slot": "02:00.0",
	 "Class": ("VGA compatible controller", 0x0300),
	 "Vendor": ("nVidia Corporation", 0x10de),
	 "Device": ("G71 [GeForce 7300 GS]", 0x01df),
	 "Rev": "a1",
	 "Driver": "nouveau",
	 "Module": ["nouveau", "nvidiafb"]}
	)


class LspciParsingTestCase(unittest.TestCase):

	"""Tests for getting data about PCI devices."""

	def test_parsing(self):
		"""Check if example lspci output is parsed correctly"""
		from hclient.machine import parse_lspci
		self.assertEqual(tuple(parse_lspci(_LSPCI_OUTPUT)),
						 _PARSED_LSPCI_OUTPUT)


_LSUSB_OUTPUT = """
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            9 Hub
  bDeviceSubClass         0 Unused
  bDeviceProtocol         0 Full speed (or root) hub
  bMaxPacketSize0        64
  idVendor           0x1d6b Linux Foundation
  idProduct          0x0002 2.0 root hub
  bcdDevice            3.00
  iManufacturer           3 
  iProduct                2 
  iSerial                 1 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           25
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xe0
      Self Powered
      Remote Wakeup
    MaxPower                0mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         9 Hub
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0 Full speed (or root) hub
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0004  1x 4 bytes
        bInterval              12

Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            9 Hub
  bDeviceSubClass         0 Unused
  bDeviceProtocol         0 Full speed (or root) hub
  bMaxPacketSize0        64
  idVendor           0x1d6b Linux Foundation
  idProduct          0x0002 2.0 root hub
  bcdDevice            3.00
  iManufacturer           3 
  iProduct                2 
  iSerial                 1 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           25
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xe0
      Self Powered
      Remote Wakeup
    MaxPower                0mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         9 Hub
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0 Full speed (or root) hub
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0004  1x 4 bytes
        bInterval              12

Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            9 Hub
  bDeviceSubClass         0 Unused
  bDeviceProtocol         0 Full speed (or root) hub
  bMaxPacketSize0        64
  idVendor           0x1d6b Linux Foundation
  idProduct          0x0001 1.1 root hub
  bcdDevice            3.00
  iManufacturer           3 
  iProduct                2 
  iSerial                 1 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           25
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xe0
      Self Powered
      Remote Wakeup
    MaxPower                0mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         9 Hub
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0 Full speed (or root) hub
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0002  1x 2 bytes
        bInterval             255

Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            9 Hub
  bDeviceSubClass         0 Unused
  bDeviceProtocol         0 Full speed (or root) hub
  bMaxPacketSize0        64
  idVendor           0x1d6b Linux Foundation
  idProduct          0x0001 1.1 root hub
  bcdDevice            3.00
  iManufacturer           3 
  iProduct                2 
  iSerial                 1 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           25
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xe0
      Self Powered
      Remote Wakeup
    MaxPower                0mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         9 Hub
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0 Full speed (or root) hub
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0002  1x 2 bytes
        bInterval             255

Bus 004 Device 002: ID 1c4f:0002 SiGma Micro 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x1c4f SiGma Micro
  idProduct          0x0002 
  bcdDevice            1.10
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           59
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower               98mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      54
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              10
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      50
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0003  1x 3 bytes
        bInterval              10

Bus 003 Device 002: ID 056a:00d6 Wacom Co., Ltd 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x056a Wacom Co., Ltd
  idProduct          0x00d6 
  bcdDevice            1.11
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           59
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower               98mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      2 Mouse
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.00
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength     176
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0009  1x 9 bytes
        bInterval               4
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.00
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      75
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               4
"""

_PARSED_LSUSB_OUTPUT = (
	{'classId': '09',
	 'vendorId': '1d6b',
	 'subclassName': 'Unused',
	 'protocolId': '00',
	 'productName': '2.0 root hub',
	 'className': 'Hub',
	 'protocolName': 'Full speed (or root) hub',
	 'subclassId': '00',
	 'vendorName': 'Linux Foundation',
	 'productId': '0002'},
	{'classId': '09',
	 'vendorId': '1d6b',
	 'subclassName': 'Unused',
	 'protocolId': '00',
	 'productName': '2.0 root hub',
	 'className': 'Hub',
	 'protocolName': 'Full speed (or root) hub',
	 'subclassId': '00',
	 'vendorName': 'Linux Foundation',
	 'productId': '0002'},
	{'classId': '09',
	 'vendorId': '1d6b',
	 'subclassName': 'Unused',
	 'protocolId': '00',
	 'productName': '1.1 root hub',
	 'className': 'Hub',
	 'protocolName': 'Full speed (or root) hub',
	 'subclassId': '00',
	 'vendorName': 'Linux Foundation',
	 'productId': '0001'},
	{'classId': '09',
	 'vendorId': '1d6b',
	 'subclassName': 'Unused',
	 'protocolId': '00',
	 'productName': '1.1 root hub',
	 'className': 'Hub',
	 'protocolName': 'Full speed (or root) hub',
	 'subclassId': '00',
	 'vendorName': 'Linux Foundation',
	 'productId': '0001'},
	{'classId': '03',
	 'vendorId': '1c4f',
	 'subclassName': 'No Subclass',
	 'protocolId': '00',
	 'productName': '',
	 'className': 'Human Interface Device',
	 'protocolName': 'None',
	 'subclassId': '00',
	 'vendorName': 'SiGma Micro',
	 'productId': '0002'},
	{'classId': '03',
	 'vendorId': '056a',
	 'subclassName': 'No Subclass',
	 'protocolId': '00',
	 'productName': '',
	 'className': 'Human Interface Device',
	 'protocolName': 'None',
	 'subclassId': '00',
	 'vendorName': 'Wacom Co., Ltd',
	 'productId': '00d6'})


class LsusbParsingTestCase(unittest.TestCase):

	"""Tests for getting data about USB devices."""

	def test_parsing(self):
		"""Check if example lsusb output is parsed correctly"""
		from hclient.machine import parse_lsusb
		self.assertEqual(tuple(parse_lsusb(_LSUSB_OUTPUT)),
						 _PARSED_LSUSB_OUTPUT)


_USB_DEVICE_DATA = """Bus 002 Device 003: ID 13b1:0020 Linksys WUSB54GC v1 802.11g Adapter [Ralink RT73]
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x13b1 Linksys
  idProduct          0x0020 WUSB54GC v1 802.11g Adapter [Ralink RT73]
  bcdDevice            0.01
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           32
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              300mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass    255 Vendor Specific Subclass
      bInterfaceProtocol    255 Vendor Specific Protocol
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
"""


_LSUSB_OUTPUT_SINGLEDIGIT_SUBCLASS = """Bus 003 Device 003: ID 0483:2016 SGS Thomson Microelectronics Fingerprint Reader
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x0483 SGS Thomson Microelectronics
  idProduct          0x2016 Fingerprint Reader
  bcdDevice            0.01
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           39
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           3
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0004  1x 4 bytes
        bInterval              20
"""


_DEVICES_SINGLEDIGIT_SUBCLASS = ({
	'classId': 'ff',
	'vendorId': '0483',
	'subclassName': '',
	'protocolId': '00',
	'productName': 'Fingerprint Reader',
	'className': 'Vendor Specific Class',
	'protocolName': '',
	'subclassId': '00',
	'vendorName': 'SGS Thomson Microelectronics',
	'productId': '2016'},)


class CreatingDevicesTestCase(unittest.TestCase):

	"""Tests for creating devices."""

	# TODO change various APIs so it will be easy to check what
	# devices will be made using example lspci and lsusb outputs.

	def test_dict(self):
		"""Test that a dictionary of devices is created"""
		from hclient.machine import createDevices
		self.assertTrue(isinstance(createDevices(), dict))

	def test_usb_device(self):
		"""Test that an USB device can be found"""
		from hclient import machine
		usb_devices = machine.parse_lsusb(_USB_DEVICE_DATA)
		devices = machine.createDevices(pci_devices=(),
										usb_devices=usb_devices)
		self.assertEquals(len(devices), 1)

	def test_no_usb_devices(self):
		"""Test that non-reportable USB devices are not listed"""
		from hclient.machine import createDevices
		devices = createDevices(pci_devices=(),
								usb_devices=_PARSED_LSUSB_OUTPUT)
		self.assertEquals(devices, {})

	def test_singledigit_subclass(self):
		"""Test that single digit unnamed subclass numbers are accepted"""
		from hclient import machine
		usb_devices = machine.parse_lsusb(_LSUSB_OUTPUT_SINGLEDIGIT_SUBCLASS)
		self.assertEquals(tuple(usb_devices), _DEVICES_SINGLEDIGIT_SUBCLASS)

# Local Variables:
# indent-tabs-mode: t
# python-guess-indent: nil
# python-indent: 4
# tab-width: 4
# End:
