Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app): add network interface collection to system-info #5764

Merged
merged 3 commits into from
May 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 73 additions & 20 deletions app-shell/src/system-info/__tests__/dispatch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as SystemInfo from '@opentrons/app/src/system-info'
import { uiInitialized } from '@opentrons/app/src/shell'
import * as OS from '../../os'
import * as UsbDevices from '../usb-devices'
import * as NetworkInterfaces from '../network-interfaces'
import { registerSystemInfo } from '..'

import type {
Expand All @@ -14,8 +15,15 @@ import type {
UsbDeviceMonitorOptions,
} from '../usb-devices'

import type {
NetworkInterface,
NetworkInterfaceMonitor,
NetworkInterfaceMonitorOptions,
} from '../network-interfaces'

jest.mock('../../os')
jest.mock('../usb-devices')
jest.mock('../network-interfaces')

const createUsbDeviceMonitor: JestMockFn<
[UsbDeviceMonitorOptions | void],
Expand All @@ -25,15 +33,23 @@ const createUsbDeviceMonitor: JestMockFn<
const getWindowsDriverVersion: JestMockFn<[Device], any> =
UsbDevices.getWindowsDriverVersion

const getActiveInterfaces: JestMockFn<[], Array<NetworkInterface>> =
NetworkInterfaces.getActiveInterfaces

const createNetworkInterfaceMonitor: JestMockFn<
[NetworkInterfaceMonitorOptions],
NetworkInterfaceMonitor
> = NetworkInterfaces.createNetworkInterfaceMonitor

const isWindows: JestMockFn<[], boolean> = OS.isWindows

const flush = () => new Promise(resolve => setTimeout(resolve, 0))

describe('app-shell::system-info module action tests', () => {
const dispatch = jest.fn()
const getAllDevices: JestMockFn<[], any> = jest.fn()
const stop = jest.fn()
const monitor: $Shape<UsbDeviceMonitor> = { getAllDevices, stop }
const usbMonitor: UsbDeviceMonitor = { getAllDevices, stop: jest.fn() }
const ifaceMonitor: NetworkInterfaceMonitor = { stop: jest.fn() }
const { windowsDriverVersion: _, ...notRealtek } = Fixtures.mockUsbDevice
const realtek0 = { ...notRealtek, manufacturer: 'Realtek' }
const realtek1 = { ...notRealtek, manufacturer: 'realtek' }
Expand All @@ -42,19 +58,29 @@ describe('app-shell::system-info module action tests', () => {
beforeEach(() => {
handler = registerSystemInfo(dispatch)
isWindows.mockReturnValue(false)
createUsbDeviceMonitor.mockReturnValue(monitor)
createUsbDeviceMonitor.mockReturnValue(usbMonitor)
createNetworkInterfaceMonitor.mockReturnValue(ifaceMonitor)
getAllDevices.mockResolvedValue([realtek0])
getActiveInterfaces.mockReturnValue([
Fixtures.mockNetworkInterface,
Fixtures.mockNetworkInterfaceV6,
])
})

afterEach(() => {
jest.resetAllMocks()
})

it('sends initial USB device list on shell:UI_INITIALIZED', () => {
it('sends initial USB device and network list on shell:UI_INITIALIZED', () => {
handler(uiInitialized())

return flush().then(() => {
expect(dispatch).toHaveBeenCalledWith(SystemInfo.initialized([realtek0]))
expect(dispatch).toHaveBeenCalledWith(
SystemInfo.initialized(
[realtek0],
[Fixtures.mockNetworkInterface, Fixtures.mockNetworkInterfaceV6]
)
)
expect(getWindowsDriverVersion).toHaveBeenCalledTimes(0)
})
})
Expand All @@ -65,16 +91,17 @@ describe('app-shell::system-info module action tests', () => {

return flush().then(() => {
expect(createUsbDeviceMonitor).toHaveBeenCalledTimes(1)
expect(dispatch).toHaveBeenCalledTimes(1)
expect(createNetworkInterfaceMonitor).toHaveBeenCalledTimes(1)
expect(dispatch).toHaveBeenCalledTimes(2)
})
})

it('sends systemInfo:USB_DEVICE_ADDED when device added', () => {
handler(uiInitialized())
const monitorOptions = createUsbDeviceMonitor.mock.calls[0][0]
const usbMonitorOptions = createUsbDeviceMonitor.mock.calls[0][0]

expect(monitorOptions?.onDeviceAdd).toEqual(expect.any(Function))
const onDeviceAdd = monitorOptions?.onDeviceAdd ?? noop
expect(usbMonitorOptions?.onDeviceAdd).toEqual(expect.any(Function))
const onDeviceAdd = usbMonitorOptions?.onDeviceAdd ?? noop
onDeviceAdd(realtek0)

return flush().then(() => {
Expand All @@ -85,10 +112,10 @@ describe('app-shell::system-info module action tests', () => {

it('sends systemInfo:USB_DEVICE_REMOVED when device removed', () => {
handler(uiInitialized())
const monitorOptions = createUsbDeviceMonitor.mock.calls[0][0]
const usbMonitorOptions = createUsbDeviceMonitor.mock.calls[0][0]

expect(monitorOptions?.onDeviceRemove).toEqual(expect.any(Function))
const onDeviceRemove = monitorOptions?.onDeviceRemove ?? noop
expect(usbMonitorOptions?.onDeviceRemove).toEqual(expect.any(Function))
const onDeviceRemove = usbMonitorOptions?.onDeviceRemove ?? noop
onDeviceRemove(realtek0)

return flush().then(() => {
Expand All @@ -98,6 +125,28 @@ describe('app-shell::system-info module action tests', () => {
})
})

it('sends systemInfo:NETWORK_INTERFACES_CHANGED when ifaces change', () => {
handler(uiInitialized())
const ifaceMonitorOpts = createNetworkInterfaceMonitor.mock.calls[0][0]

expect(ifaceMonitorOpts.onInterfaceChange).toEqual(expect.any(Function))
const { onInterfaceChange } = ifaceMonitorOpts

onInterfaceChange([
Fixtures.mockNetworkInterface,
Fixtures.mockNetworkInterfaceV6,
])

return flush().then(() => {
expect(dispatch).toHaveBeenCalledWith(
SystemInfo.networkInterfacesChanged([
Fixtures.mockNetworkInterface,
Fixtures.mockNetworkInterfaceV6,
])
)
})
})

it('stops monitoring on app quit', () => {
handler(uiInitialized())

Expand All @@ -107,7 +156,8 @@ describe('app-shell::system-info module action tests', () => {

expect(typeof appQuitHandler).toBe('function')
appQuitHandler()
expect(monitor.stop).toHaveBeenCalled()
expect(usbMonitor.stop).toHaveBeenCalled()
expect(ifaceMonitor.stop).toHaveBeenCalled()
})

describe('on windows', () => {
Expand All @@ -125,20 +175,23 @@ describe('app-shell::system-info module action tests', () => {
expect(getWindowsDriverVersion).toHaveBeenCalledWith(realtek1)

expect(dispatch).toHaveBeenCalledWith(
SystemInfo.initialized([
{ ...realtek0, windowsDriverVersion: '1.2.3' },
notRealtek,
{ ...realtek1, windowsDriverVersion: '1.2.3' },
])
SystemInfo.initialized(
[
{ ...realtek0, windowsDriverVersion: '1.2.3' },
notRealtek,
{ ...realtek1, windowsDriverVersion: '1.2.3' },
],
[Fixtures.mockNetworkInterface, Fixtures.mockNetworkInterfaceV6]
)
)
})
})

it('should add Windows driver versions to Realtek devices on add', () => {
getAllDevices.mockResolvedValue([])
handler(uiInitialized())
const monitorOptions = createUsbDeviceMonitor.mock.calls[0][0]
const onDeviceAdd = monitorOptions?.onDeviceAdd ?? noop
const usbMonitorOptions = createUsbDeviceMonitor.mock.calls[0][0]
const onDeviceAdd = usbMonitorOptions?.onDeviceAdd ?? noop
onDeviceAdd(realtek0)

return flush().then(() => {
Expand Down
135 changes: 135 additions & 0 deletions app-shell/src/system-info/__tests__/network-interfaces.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// @flow
import os from 'os'
import noop from 'lodash/noop'

import {
getActiveInterfaces,
createNetworkInterfaceMonitor,
} from '../network-interfaces'

jest.mock('os')

const networkInterfaces: JestMockFn<
[],
{ [ifName: string]: Array<os$NetIFAddr>, ... }
> = os.networkInterfaces

const mockV4 = {
address: '192.168.1.17',
netmask: '255.255.255.0',
family: 'IPv4',
mac: 'f8:ff:c2:46:59:80',
internal: false,
cidr: '192.168.1.17/24',
}

const mockV6 = {
address: 'fe80::8e0:61a3:8bde:7385',
netmask: 'ffff:ffff:ffff:ffff::',
family: 'IPv6',
mac: 'f8:ff:c2:46:59:80',
internal: false,
cidr: 'fe80::8e0:61a3:8bde:7385/64',
scopeid: 6,
}

describe('system-info::network-interfaces', () => {
beforeEach(() => {
jest.useFakeTimers()
})

afterEach(() => {
jest.resetAllMocks()
jest.clearAllTimers()
jest.useRealTimers()
})

it('should return external network interfaces', () => {
networkInterfaces.mockReturnValue({
en0: [mockV4, mockV6],
en1: [mockV6],
lo0: [{ ...mockV4, internal: true }, { ...mockV6, internal: true }],
})

expect(getActiveInterfaces()).toEqual([
{ name: 'en0', ...mockV4 },
{ name: 'en0', ...mockV6 },
{ name: 'en1', ...mockV6 },
])
})

it('should be able to poll the attached network interfaces', () => {
networkInterfaces.mockReturnValue({})

const monitor = createNetworkInterfaceMonitor({
pollInterval: 30000,
onInterfaceChange: noop,
})

expect(networkInterfaces).toHaveBeenCalledTimes(1)
jest.advanceTimersByTime(30000)
expect(networkInterfaces).toHaveBeenCalledTimes(2)
jest.advanceTimersByTime(30000)
expect(networkInterfaces).toHaveBeenCalledTimes(3)

monitor.stop()
jest.advanceTimersByTime(30000)
expect(networkInterfaces).toHaveBeenCalledTimes(3)
})

it('should be able to signal interface changes', () => {
const handleInterfaceChange = jest.fn()

networkInterfaces.mockReturnValue({})

createNetworkInterfaceMonitor({
pollInterval: 30000,
onInterfaceChange: handleInterfaceChange,
})

networkInterfaces.mockReturnValueOnce({
en0: [mockV4, mockV6],
})
jest.advanceTimersByTime(30000)
expect(handleInterfaceChange).toHaveBeenCalledWith([
{ name: 'en0', ...mockV4 },
{ name: 'en0', ...mockV6 },
])
handleInterfaceChange.mockClear()

networkInterfaces.mockReturnValueOnce({
en0: [mockV4, mockV6],
})
jest.advanceTimersByTime(30000)
expect(handleInterfaceChange).toHaveBeenCalledTimes(0)
handleInterfaceChange.mockClear()

networkInterfaces.mockReturnValueOnce({
en0: [mockV4, mockV6],
en1: [mockV4],
})
jest.advanceTimersByTime(30000)
expect(handleInterfaceChange).toHaveBeenCalledWith([
{ name: 'en0', ...mockV4 },
{ name: 'en0', ...mockV6 },
{ name: 'en1', ...mockV4 },
])
handleInterfaceChange.mockClear()
})

it('should be able to stop monitoring interface changes', () => {
const handleInterfaceChange = jest.fn()

networkInterfaces.mockReturnValue({})

const monitor = createNetworkInterfaceMonitor({
pollInterval: 30000,
onInterfaceChange: handleInterfaceChange,
})

networkInterfaces.mockReturnValueOnce({ en0: [mockV4] })
monitor.stop()
jest.advanceTimersByTime(30000)
expect(handleInterfaceChange).toHaveBeenCalledTimes(0)
})
})
Loading