ooni-probe-cli/QA/lib/analysis.mjs
Simone Basso da34cfe6c9
feat(QA): add test cases for websteps vs webconnectivity (#583)
This pull request introduces a set of Node.js scripts for performing A/B comparison of websteps and webconnectivity as described in https://github.com/ooni/probe/issues/1805. Rather than using Jafar, I ended up using `miniooni`'s `--censor` command line flag introduced in [v3.12.0-alpha.1](https://github.com/ooni/probe-cli/releases/tag/v3.12.0-alpha.1). The main reason for doing so is that it's simpler to run tests without requiring root access and Linux _and_ Docker (e.g., I did not develop part of this diff using Linux). Additionally, I choose to use Node.js rather than extending the existing Python framework for QA, because I found Node.js easier when working with JSON data.
2021-11-05 15:56:04 +01:00

197 lines
7.3 KiB
JavaScript

// This file contains code for analysing results.
import { type } from "os"
// checkAnnotations checks whether we have annotations.
function checkAnnotations(report) {
let result = true
const isObject = typeof (report.annotations) === "object"
console.log(`checking whether annotations is an object... ${isObject}`)
result = result && isObject
const hasArchitecture = typeof (report.annotations.architecture) === "string"
console.log(`checking whether annotations contains architecture... ${hasArchitecture}`)
result = result && hasArchitecture
const hasEngineName = typeof (report.annotations.engine_name) === "string"
console.log(`checking whether annotations contains engine_name... ${hasEngineName}`)
result = result && hasEngineName
const hasEngineVersion = typeof (report.annotations.engine_version) === "string"
console.log(`checking whether annotations contains engine_version... ${hasEngineVersion}`)
result = result && hasEngineVersion
const hasPlatform = typeof (report.annotations.platform) === "string"
console.log(`checking whether annotations contains platform... ${hasPlatform}`)
result = result && hasPlatform
return result
}
// checkDataFormatVersion checks whether we have data_format_version.
function checkDataFormatVersion(report) {
const result = report.data_format_version === "0.2.0"
console.log(`checking whether we have the right data format version... ${result}`)
return result
}
// checkExtensions ensures that extensions exists and has the right type.
function checkExtensions(report) {
const result = typeof (report.extensions) === "object"
console.log(`checking whether report.extensions is an object... ${result}`)
// Quirk: some experiments (e.g. web_connectivity) don't include
// an extensions object, and we don't want to fail here.
return true
}
// checkInput ensures that the input is correct.
function checkInput(testCase, report) {
const result = testCase.input === report.input
console.log(`checking whether input is correct... ${result}`)
return result
}
// checkExperimentName ensures that the experimentName is correctly
// set in the output report file.
function checkExperimentName(name, report) {
const result = report.test_name === name
console.log(`checking whether the experiment name is correct... ${result}`)
return result
}
// checkStartTime ensures that a given start time is in the correct format.
function checkStartTime(report, key) {
const value = report[key]
let result = typeof (value) === "string"
console.log(`checking whether ${key} is a string... ${result}`)
result = result && value.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/) !== null
console.log(`checking whether ${key} matches the date regexp... ${result}`)
return result
}
// checkASN ensures that an ASN field is correct.
function checkASN(report, key) {
const value = report[key]
let result = typeof (value) === "string"
console.log(`checking whether ${key} is a string... ${result}`)
result = result && value.match(/^AS[0-9]+$/) !== null
console.log(`checking whether ${key} matches the ASN regexp... ${result}`)
return result
}
// checkProbeCC ensures that the probe_cc field is correct.
function checkProbeCC(report) {
const value = report["probe_cc"]
let result = typeof (value) === "string"
console.log(`checking whether probe_cc is a string... ${result}`)
result = result && value.match(/^[A-Z]{2}$/) !== null
console.log(`checking whether probe_cc matches the CC regexp... ${result}`)
return result
}
// checkProbeIP ensures that the probe_ip field is correct.
function checkProbeIP(report) {
const result = report.probe_ip === "127.0.0.1"
console.log(`checking whether probe_ip is correct... ${result}`)
return result
}
// checkReportID ensures that the report_id field is correct.
function checkReportID(report) {
const result = report.report_id === "" // note: we don't submit
console.log(`checking whether report_id is correct... ${result}`)
return result
}
// checkString ensures that a field is a string.
function checkString(report, key) {
const value = report[key]
const result = typeof(value) === "string"
console.log(`checking whether ${key} is a string... ${result}`)
return result
}
// checkNetworkName ensures that an xxx_network_name field is correct.
function checkNetworkName(report, key) {
return checkString(report, key)
}
// checkResolverIP ensures that the resolver_ip field is correct.
function checkResolverIP(report) {
return checkString(report, "resolver_ip")
}
// checkSoftwareName ensures that the software_name field is correct.
function checkSoftwareName(report) {
return checkString(report, "software_name")
}
// checkSoftwareVersion ensures that the software_version field is correct.
function checkSoftwareVersion(report) {
return checkString(report, "software_version")
}
// checkTestRuntime ensures that the test_runtime field is correct.
function checkTestRuntime(report) {
const result = typeof(report.test_runtime) === "number"
console.log(`checking whether test_runtime is a number... ${result}`)
return result
}
// checkTestVersion ensures that the test_version field is correct.
function checkTestVersion(report) {
return checkString(report, "test_version")
}
// checkTestKeys ensures that test_keys is an object.
function checkTestKeys(report) {
const result = typeof(report.test_keys) === "object"
console.log(`checking whether test_keys is an object... ${result}`)
return result
}
// checkMeasurement is a function that invokes a standard set of checks
// to validate the top-level test keys of a result.
//
// This function helps to implement per-experiment checkers.
//
// Arguments:
//
// - testCase is the current test case
//
// - name is the name of the current experiment
//
// - report is the JSON measurement
//
// - extraChecks is the optional callback to perform extra checks
// that takes in input the report's test keys.
export function checkMeasurement(testCase, name, report, extraChecks) {
let result = true
result = result && checkAnnotations(report)
result = result && checkDataFormatVersion(report)
result = result && checkExtensions(report)
result = result && checkInput(testCase, report)
result = result && checkStartTime(report, "measurement_start_time")
result = result && checkASN(report, "probe_asn")
result = result && checkProbeCC(report)
result = result && checkProbeIP(report)
result = result && checkNetworkName(report, "probe_network_name")
result = result && checkReportID(report)
result = result && checkASN(report, "resolver_asn")
result = result && checkResolverIP(report)
result = result && checkNetworkName(report, "resolver_network_name")
result = result && checkSoftwareName(report)
result = result && checkSoftwareVersion(report)
result = result && checkExperimentName(name, report)
result = result && checkTestRuntime(report)
result = result && checkStartTime(report, "test_start_time")
result = result && checkTestVersion(report)
result = result && checkTestKeys(report)
if (typeof extraChecks === "function") {
result = result && extraChecks(report.test_keys)
}
return result
}