1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
// Data models (interfaces)
import { Config } from '../models/config.model'
import { Adapter } from '../models/adapter.model'
import { TestService } from '../models/test-service.model'
import { Reporter } from '../models/reporter.model'
import { Report } from '../models/report.model'
import { Context } from '../models/context.model'
import { ExecState } from '../models/exec-state.model'
// Physical hardware adapters (a standard Telus one for now)
import { ActiontecT3200M } from '../adapters/actiontec-t3200m'
// Test providers
import { Ookla } from '../test-services/ookla'
import { Fast } from '../test-services/fast'
// Data reporters
import { Firebase } from '../reporters/firebase'
import { Scheduler } from '../scheduler'
const adapterMap = {
'actiontec-t3200m': ActiontecT3200M
}
const testServiceMap = {
ookla: Ookla,
fast: Fast
}
const reportersMap = {
firebase: Firebase
}
const mapToObj = (keys: string[], mapObj: Object) =>
keys.map(k => mapObj[k]).filter(k => k != null)
export const testRunner = async (ctx: Context) => {
const { config } = ctx
// Users configure which adapters/services/reporters
// they want to use in Context.config. These "plugins"
// are configured using strings, so we use "map" objects
// to map a string to a class, and then create an instance
// of each class with our current Context object
const newWithCtx = k => new k(ctx)
const makeTestServices = (testProviders: string[]): TestService[] =>
mapToObj(testProviders, testServiceMap).map(newWithCtx)
const makeReporters = (reporters: string[]): Reporter[] =>
mapToObj(reporters, reportersMap).map(newWithCtx)
const adapterClass = adapterMap[config.adapter]
// only 'new' it if we found an adapter in the map
const adapter = adapterClass ? new adapterClass(ctx) : undefined
const services = makeTestServices(config.testServices)
const scheduler = new Scheduler(ctx, adapter)
const testCallback = async (e: ExecState) => {
const reporters = makeReporters(config.reporters)
const reports = await testEachService(adapter, services, ctx)
reportEach(reporters, reports, ctx)
}
scheduler.subscribe(testCallback)
}
export const reportEach = async (
reporters: Reporter[],
reports: Report[],
ctx: Context
): Promise<void> => {
for (let reporter of reporters) {
ctx.logger(`Reporting results to ${reporter.name}`)
for (let report of reports) {
reporter.record(report)
}
await reporter.publish()
}
}
export const testEachService = async (
adapter: Adapter,
services: TestService[],
ctx: Context
): Promise<Report[]> => {
let reports = []
for (let service of services) {
ctx.logger(`Testing ${service.name}`)
const report = await testService(adapter, service, ctx)
reports.push(report)
}
return reports
}
export const testService = async (
adapter: Adapter,
service: TestService,
ctx: Context
): Promise<Report> => {
const nConnectedClients = await adapter.nConnectedClients()
const uptime = await adapter.uptime()
const leaseTime = await adapter.leaseTime()
const download = await service.testDownload()
const upload = await service.testUpload()
const datetime = Date.now() / 1000
return {
nConnectedClients,
uptime,
leaseTime,
download,
upload,
datetime,
adapter: adapter.name,
service: service.name
}
}
|