🏡 index : cold-air-networks/mirror-jetstream.git

"use strict";

/*
 * Copyright (C) 2025 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
*/

const defaultEmptyMap = Object.freeze({});

class Params {
    // Enable a detailed developer menu to change the current Params.
    developerMode = false;
    startAutomatically = false;
    report = false;
    startDelay = undefined;

    testList = [];
    testIterationCount = undefined;
    testWorstCaseCount = undefined;
    prefetchResources = true;

    // Display group details.
    groupDetails = false

    RAMification = false;
    dumpJSONResults = false;
    dumpTestList = false;
    // Override iteration and worst-case counts per workload.
    // Example:
    //   testIterationCountMap = { "acorn-wtb": 5 };
    testIterationCountMap = defaultEmptyMap;
    testWorstCaseCountMap = defaultEmptyMap;

    customPreIterationCode = undefined;
    customPostIterationCode = undefined;

    constructor(sourceParams = undefined) {
        if (sourceParams)
            this._copyFromSearchParams(sourceParams);
        if (!this.developerMode)
            Object.freeze(this);
    }

    _copyFromSearchParams(sourceParams) {
        this.startAutomatically = this._parseBooleanParam(sourceParams, "startAutomatically");
        this.developerMode = this._parseBooleanParam(sourceParams, "developerMode");
        this.report = this._parseBooleanParam(sourceParams, "report");
        this.prefetchResources = this._parseBooleanParam(sourceParams, "prefetchResources");
        this.RAMification = this._parseBooleanParam(sourceParams, "RAMification");
        this.dumpJSONResults = this._parseBooleanParam(sourceParams, "dumpJSONResults");
        this.groupDetails = this._parseBooleanParam(sourceParams, "groupDetails");
        this.dumpTestList = this._parseBooleanParam(sourceParams, "dumpTestList");

        this.customPreIterationCode = this._parseStringParam(sourceParams, "customPreIterationCode");
        this.customPostIterationCode = this._parseStringParam(sourceParams, "customPostIterationCode");

        this.startDelay = this._parseIntParam(sourceParams, "startDelay", 0);
        if (!this.startDelay) {
            if (this.report)
                 this.startDelay = 4000;
            if (this.startAutomatically)
                 this.startDelay = 100;
        }

        this.testList = this._parseOneOf(sourceParams, ["testList", "tag", "tags", "test", "tests"], this._parseTestListParam);
        this.testIterationCount = this._parseOneOf(sourceParams, ["testIterationCount", "iterationCount", "iterations" ], this._parseIntParam, 1);
        this.testWorstCaseCount = this._parseOneOf(sourceParams, ["testWorstCaseCount", "worstCaseCount", "worst"], this._parseIntParam, 1);

        const unused = Array.from(sourceParams.keys());
        if (unused.length > 0)
            console.error("Got unused source params", unused);
    }

    _parseOneOf(sourceParams, paramKeys, parseFunction, ...args) {
        const defaultParamKey = paramKeys[0]
        let result = undefined;
        let parsedParamKey = undefined;
        for (const paramKey of paramKeys) {
            if (!sourceParams.has(paramKey)) {
                continue;
            }
            const parseResult = parseFunction.call(this, sourceParams, paramKey, ...args);
            if (parsedParamKey) {
                throw new Error(`Cannot parse ${paramKey}, overriding previous "${parsedParamKey}" value ${JSON.stringify(result)} with ${JSON.stringify(parseResult)}`)
            }
            parsedParamKey = paramKey;
            result = parseResult;
        }
        if (!parsedParamKey) {
            return DefaultJetStreamParams[defaultParamKey];
        }
        return result;
    }

    _parseTestListParam(sourceParams, key) {
        if (!sourceParams.has(key))
            return this.testList;
        let testList = [];
        if (sourceParams?.getAll) {
            for (const param of sourceParams?.getAll(key)) {
                testList.push(...param.split(","));
            }
        } else {
            // fallback for cli sourceParams which is just a Map;
            testList = sourceParams.get(key).split(",");
        }
        testList = testList.map(each => each.trim());
        sourceParams.delete(key);
        return testList;
    }

    _parseStringParam(sourceParams, paramKey) {
        if (!sourceParams.has(paramKey))
            return DefaultJetStreamParams[paramKey];
        const value = sourceParams.get(paramKey);
        sourceParams.delete(paramKey);
        return value;
    }

    _parseBooleanParam(sourceParams, paramKey) {
        if (!sourceParams.has(paramKey))
            return DefaultJetStreamParams[paramKey];
        const rawValue = sourceParams.get(paramKey);;
        sourceParams.delete(paramKey);
        const value = rawValue.toLowerCase();
        return !(value === "false" || value === "0");
    }

    _parseIntParam(sourceParams, paramKey, minValue) {
        if (!sourceParams.has(paramKey))
            return DefaultJetStreamParams[paramKey];

        const parsedValue = this._parseInt(sourceParams.get(paramKey), paramKey);
        if (parsedValue < minValue)
            throw new Error(`Invalid ${paramKey} param: '${parsedValue}', value must be >= ${minValue}.`);
        sourceParams.delete(paramKey);
        return parsedValue;
    }

    _parseInt(value, errorMessage) {
        const number = Number(value);
        if (!Number.isInteger(number) && errorMessage)
            throw new Error(`Invalid ${errorMessage} param: '${value}', expected int.`);
        return parseInt(number);
    }

    get isDefault() {
      return this === DefaultJetStreamParams;
    }

    get nonDefaultParams() {
        const diff = Object.create(null);
        for (const [key, value] of Object.entries(this)) {
            const defaultValue = DefaultJetStreamParams[key]
            if (value == defaultValue) continue;
            if (value?.length == 0 && defaultValue?.length == 0) continue;
            diff[key] = value;
        }
        return diff;
    }
}

const DefaultJetStreamParams = new Params();
let maybeCustomParams = DefaultJetStreamParams;
if (globalThis?.JetStreamParamsSource) {
    try {
        maybeCustomParams = new Params(globalThis?.JetStreamParamsSource);
        // We might have parsed the same values again, do a poor-mans deep-equals:
        if (JSON.stringify(maybeCustomParams) === JSON.stringify(DefaultJetStreamParams)) {
           maybeCustomParams = DefaultJetStreamParams 
        }
    } catch (e) {
        console.error("Invalid Params", e, "\nUsing defaults as fallback:", maybeCustomParams);
    }
}
const JetStreamParams = maybeCustomParams;