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

add random matrix for scala test #16

Merged
merged 1 commit into from
Apr 3, 2024
Merged
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
add random matrix for scala test
get pull files from tegonal-gh-commons and
vlsi/github-actions-random-matrix
robstoll committed Apr 3, 2024

Unverified

This user has not yet uploaded their public signing key.
commit 41e499f83ad438beaab4db9aba85adb400690cd2
16 changes: 16 additions & 0 deletions .github/workflows/matrix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// __ __
// / /____ ___ ____ ___ ___ _/ / This file is provided to you by https://github.com/tegonal/scala-commons
// / __/ -_) _ `/ _ \/ _ \/ _ `/ / Copyright 2024 Tegonal Genossenschaft <[email protected]>
// \__/\__/\_, /\___/_//_/\_,_/_/ It is licensed under Apache 2.0
// /___/ Please report bugs and contribute back your improvements
//
// Version: v0.17.0
//##################################
const {MatrixBuilder} = require('./matrix_builder');
const {configureScalaDefaults, setMatrix} = require('./matrix_commons');

const matrix = new MatrixBuilder();
configureScalaDefaults(matrix)

setMatrix(matrix, 4);
236 changes: 236 additions & 0 deletions .github/workflows/matrix_builder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
// License: Apache-2.0
// Copyright Vladimir Sitnikov, 2021
// See https://github.com/vlsi/github-actions-random-matrix

class Axis {
constructor({name, title, values}) {
this.name = name;
this.title = title;
this.values = values;
// If all entries have same weight, the axis has uniform distribution
this.uniform = values.reduce((a, b) => a === (b.weight || 1) ? a : 0, values[0].weight || 1) !== 0
this.totalWeigth = this.uniform ? values.length : values.reduce((a, b) => a + (b.weight || 1), 0);
}

static matches(row, filter) {
if (typeof filter === 'function') {
return filter(row);
}
if (Array.isArray(filter)) {
// e.g. row={os: 'windows'}; filter=[{os: 'linux'}, {os: 'linux'}]
return filter.find(v => Axis.matches(row, v));
}
if (typeof filter === 'object') {
// e.g. row={jdk: {name: 'openjdk', version: 8}}; filter={jdk: {version: 8}}
for (const [key, value] of Object.entries(filter)) {
if (!row.hasOwnProperty(key) || !Axis.matches(row[key], value)) {
return false;
}
}
return true;
}
return row === filter;
}

pickValue(filter) {
let values = this.values;
if (filter) {
values = values.filter(v => Axis.matches(v, filter));
}
if (values.length === 0) {
const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter);
throw Error(`No values produces for axis '${this.name}' from ${JSON.stringify(this.values)}, filter=${filterStr}`);
}
if (values.length === 1) {
return values[0];
}
if (this.uniform) {
return values[Math.floor(Math.random() * values.length)];
}
const totalWeight = !filter ? this.totalWeigth : values.reduce((a, b) => a + (b.weight || 1), 0);
let weight = Math.random() * totalWeight;
for (let i = 0; i < values.length; i++) {
const value = values[i];
weight -= value.weight || 1;
if (weight <= 0) {
return value;
}
}
return values[values.length - 1];
}
}

class MatrixBuilder {
constructor() {
this.axes = [];
this.axisByName = {};
this.rows = [];
this.duplicates = {};
this.excludes = [];
this.includes = [];
this.failOnUnsatisfiableFilters = false;
}

/**
* Specifies include filter (all the generated rows would comply with all the include filters)
* @param filter
*/
include(filter) {
this.includes.push(filter);
}

/**
* Specifies exclude filter (e.g. exclude a forbidden combination)
* @param filter
*/
exclude(filter) {
this.excludes.push(filter);
}

addAxis({name, title, values}) {
const axis = new Axis({name, title, values});
this.axes.push(axis);
this.axisByName[name] = axis;
return axis;
}

setNamePattern(names) {
this.namePattern = names;
}

/**
* Returns true if the row matches the include and exclude filters.
* @param row input row
* @returns {boolean}
*/
matches(row) {
return (this.excludes.length === 0 || !this.excludes.find(f => Axis.matches(row, f))) &&
(this.includes.length === 0 || this.includes.find(f => Axis.matches(row, f)));
}

failOnUnsatisfiableFilters(value) {
this.failOnUnsatisfiableFilters = value;
}

/**
* Adds a row that matches the given filter to the resulting matrix.
* filter values could be
* - literal values: filter={os: 'windows-latest'}
* - arrays: filter={os: ['windows-latest', 'linux-latest']}
* - functions: filter={os: x => x!='windows-latest'}
* @param filter object with keys matching axes names
* @returns {*}
*/
generateRow(filter) {
let res;
if (filter) {
// If matching row already exists, no need to generate more
res = this.rows.find(v => Axis.matches(v, filter));
if (res) {
return res;
}
}
for (let i = 0; i < 142; i++) {
res = this.axes.reduce(
(prev, next) =>
Object.assign(prev, {
[next.name]: next.pickValue(filter ? filter[next.name] : undefined)
}),
{}
);
if (!this.matches(res)) {
continue;
}
const key = JSON.stringify(res);
if (!this.duplicates.hasOwnProperty(key)) {
this.duplicates[key] = true;
res.name =
this.namePattern.map(axisName => {
let value = res[axisName];
const title = value.title;
if (typeof title != 'undefined') {
return title;
}
const computeTitle = this.axisByName[axisName].title;
return computeTitle ? computeTitle(value) : value;
}).filter(Boolean).join(", ");
this.rows.push(res);
return res;
}
}
const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter);
const msg = `Unable to generate row for ${filterStr}. Please check include and exclude filters`;
if (this.failOnUnsatisfiableFilters) {
throw Error(msg);
} else {
console.warn(msg);
}
}

generateRows(maxRows, filter) {
for (let i = 0; this.rows.length < maxRows && i < maxRows; i++) {
this.generateRow(filter);
}
return this.rows;
}

/**
* Computes the number of all the possible combinations.
* @returns {{bad: number, good: number}}
*/
summary() {
let position = -1;
let indices = [];
let values = {};
const axes = this.axes;
function resetValuesUpTo(nextPosition) {
for(let i=0; i<nextPosition; i++) {
const axis = axes[i];
values[axis.name] = axis.values[0];
indices[i] = 1; // next index
}
position = 0;
}

function nextAvailablePosition() {
let size = axes.length;
for (let i = position; i < size; i++) {
if (indices[i] < axes[i].values.length) {
return i;
}
}
return -1;
}
// The first initialization of the values
resetValuesUpTo(this.axes.length);
let good = 0;
let bad = 0;
while (true) {
if (indices[position] < this.axes[position].values.length) {
// Advance iterator at the current position if possible
const axis = this.axes[position];
values[axis.name] = axis.values[indices[position]];
indices[position]++;
} else {
// Advance the next iterator, and reset [0..nextPosition)
position++;
let nextPosition = nextAvailablePosition();
if (nextPosition === -1) {
break;
}
const axis = this.axes[nextPosition];
values[axis.name] = axis.values[indices[nextPosition]];
indices[nextPosition]++;
resetValuesUpTo(nextPosition);
}
if (this.matches(values)) {
good++;
} else {
bad++;
}
}
return {good: good, bad: bad};
}
}

module.exports = {Axis, MatrixBuilder};
121 changes: 121 additions & 0 deletions .github/workflows/matrix_commons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//
// __ __
// / /____ ___ ____ ___ ___ _/ / This file is provided to you by https://github.com/tegonal/github-commons
// / __/ -_) _ `/ _ \/ _ \/ _ `/ / Copyright 2022 Tegonal Genossenschaft <[email protected]>
// \__/\__/\_, /\___/_//_/\_,_/_/ It is licensed under Apache License 2.0
// /___/ Please report bugs and contribute back your improvements
//
// Version: v2.5.0
//##################################
// adapted version of https://github.com/vlsi/github-actions-random-matrix/blob/main/examples/matrix.js
// licensed under Apache License 2.0
//##################################
const os = require("os");
const fs = require("fs");

const javaDistributionAxis = {
name: 'java_distribution',
values: [
'corretto',
'liberica',
'microsoft',
'semeru',
'temurin',
'zulu',
]
};

const javaVersionAxis = javaVersionAxisBuilder(['11', '17', '21']);

function javaVersionAxisBuilder(values){
return {
name: 'java_version',
title: x => 'Java ' + x,
values: values
}
}

const osAxis = {
name: 'os',
title: x => x.replace('-latest', ''),
values: [
'ubuntu-latest',
'windows-latest',
'macos-latest'
]
}

function generateJavaMinMaxRows(matrix) {
// generate one with oldest java and one with newest java version
matrix.generateRow({java_version: matrix.axisByName.java_version.values[0]});
matrix.generateRow({java_version: matrix.axisByName.java_version.values.slice(-1)[0]});
}

function generateUbuntuWindowsRows(matrix) {
matrix.generateRow({os: 'ubuntu-latest'});
matrix.generateRow({os: 'windows-latest'});
}

function configureJavaDefaults(matrix, distributionAxis = javaDistributionAxis, versionAxis = javaVersionAxis, operatingSystemAxis = osAxis ) {
matrix.addAxis(distributionAxis);
matrix.addAxis(versionAxis);
matrix.addAxis(operatingSystemAxis);

// This specifies the order of axes in CI job name (individual titles would be joined with a comma)
matrix.setNamePattern(['java_version', 'java_distribution', 'os']);

generateJavaMinMaxRows(matrix);
generateUbuntuWindowsRows(matrix);
}

function configureKotlinDefaults(matrix) {
const kotlinJavaDistributionAxis = {
...javaDistributionAxis,
values: javaDistributionAxis.values.filter ( x =>
// seems to have problems with kotlin https://youtrack.jetbrains.com/issue/KT-61836
x != 'semeru'
)
};
configureJavaDefaults(matrix, kotlinJavaDistributionAxis);
}

function configureScalaDefaults(matrix) {
configureJavaDefaults(matrix);
}

// see https://github.com/actions/toolkit/issues/1218
function setOutput(key, value) {
// Temporary hack until core actions library catches up with github new recommendations
const output = process.env['GITHUB_OUTPUT']
if (output !== undefined){
fs.appendFileSync(output, `${key}=${value}${os.EOL}`)
} else{
console.log('::set-output name=' + key + '::' + value);
}
}

function setMatrix(matrix, numberOfJobs) {
const include = matrix.generateRows(process.env.MATRIX_JOBS || numberOfJobs);
if (include.length === 0) {
throw new Error('Matrix list is empty');
}

// Sort jobs by name, however, numeric parts are sorted appropriately
// For instance, 'windows 8' would come before 'windows 11'
include.sort((a, b) => a.name.localeCompare(b.name, undefined, {numeric: true}));

console.log(include);
setOutput('matrix', JSON.stringify({include}));
}

module.exports = {
javaDistributionAxis,
javaVersionAxis,
osAxis,
setMatrix,
generateJavaMinMaxRows,
generateUbuntuWindowsRows,
configureJavaDefaults,
configureKotlinDefaults,
configureScalaDefaults,
};
Loading