First Commit

This commit is contained in:
2025-11-18 14:18:26 -07:00
parent 33eb6e3707
commit 27277ec342
6106 changed files with 3571167 additions and 0 deletions

View File

@@ -0,0 +1,331 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
/// A script to generate a random layout
/// $ dart tests/layout_fuzzer.dart layout_schema.json
import 'dart:convert';
import 'dart:io';
import 'dart:math';
final _random = Random();
const int _min_children_recursion_depth = 2;
const int _max_children_recursion_depth = 5;
int _current_children_recursion_depth = 0;
int _current_children_max_recursion_depth = 0;
int _current_string_count = 0;
// Returns between min and max, inclusive.
int generateNumber(int min, int max) {
final int n = max - min + 1;
return _random.nextInt(n) + min;
}
randomArrayElement(var array) {
final index = generateNumber(0, array.length - 1);
return array[index];
}
class Schema {
final jsonSchema;
final String propertyName;
late final String type;
Schema(this.jsonSchema, this.propertyName) {
type = jsonSchema["type"];
}
static Schema fromJson(var jsonSchema, var propertyName) {
if (jsonSchema["\$ref"] != null) {
return Fuzzer.self.schemaForDefinition(jsonSchema["\$ref"], propertyName);
}
final type = jsonSchema["type"];
if (type == "integer") {
return IntegerSchema(jsonSchema, propertyName);
} else if (type == "number") {
return NumberSchema(jsonSchema, propertyName);
} else if (type == "boolean") {
return BooleanSchema(jsonSchema, propertyName);
} else if (type == "array") {
return ArraySchema(jsonSchema, propertyName);
} else if (type == "object") {
return ObjectSchema(jsonSchema, propertyName);
} else if (type == "string") {
return StringSchema(jsonSchema, propertyName);
} else if (["lastOverlayedGeometries", "affinities"]
.contains(propertyName)) {
// lastOverlayedGeometries actually has "types" = [null, "array"],
// but we can just use an empty array instead of honouring that null
jsonSchema["type"] = "array";
return ArraySchema(jsonSchema, propertyName);
}
throw "fromJson: Unsupported type=${type}; propName=$propertyName; json=${jsonSchema}";
}
bool isObject() {
return type == "object";
}
bool isArray() {
return type == "array";
}
bool isInteger() {
return type == "integer";
}
bool isBoolean() {
return type == "boolean";
}
bool isNumber() {
return type == "number";
}
/// generates a sample matching this schema
dynamic generate() {
throw "Reimplement me!";
}
}
class IntegerSchema extends Schema {
List<int>? _possibleValues;
IntegerSchema(var jsonSchema, String propertyName)
: super(jsonSchema, propertyName) {
if (jsonSchema["enum"] != null)
_possibleValues = List<int>.from(jsonSchema["enum"]);
}
@override
dynamic generate() {
return _possibleValues == null
? generateNumber(0, 1000)
: randomArrayElement(_possibleValues!);
}
}
class NumberSchema extends Schema {
NumberSchema(var jsonSchema, String propertyName)
: super(jsonSchema, propertyName);
@override
dynamic generate() {
// This is specific for KDDW
final dpiValues = [1, 1.5, 2, 3];
return randomArrayElement(dpiValues);
}
}
class BooleanSchema extends Schema {
BooleanSchema(var jsonSchema, String propertyName)
: super(jsonSchema, propertyName);
@override
dynamic generate() {
return generateNumber(0, 1) == 1;
}
}
class StringSchema extends Schema {
StringSchema(var jsonSchema, String propertyName)
: super(jsonSchema, propertyName);
@override
dynamic generate() {
_current_string_count++;
return "somestring$_current_string_count";
}
}
class ObjectSchema extends Schema {
late final List<String> required;
ObjectSchema(var jsonSchema, String propertyName)
: super(jsonSchema, propertyName) {
required = jsonSchema["required"] == null
? []
: List.unmodifiable(jsonSchema["required"]);
}
List<Schema> properties() {
List<Schema> props = [];
Map<String, dynamic> propsJson = jsonSchema["properties"] ?? {};
for (var e in propsJson.entries) {
props.add(Schema.fromJson(e.value, e.key));
}
return props;
}
Schema? patternProperties() {
Map<String, dynamic> props = jsonSchema["patternProperties"] ?? {};
if (props.isNotEmpty) {
final String definition = props.entries.first.value["\$ref"];
return Fuzzer.self.schemaForDefinition(definition, propertyName);
}
return null;
}
@override
dynamic generate() {
var json = {};
for (final propSchema in properties()) {
if (propSchema.propertyName == "children") {
if (_current_children_recursion_depth ==
_current_children_max_recursion_depth) {
// "children" property can have children inside, we limit
// recursion here. Usually a layout has like max 3 or so of nesting
continue;
}
_current_children_recursion_depth++;
}
json[propSchema.propertyName] = propSchema.generate();
}
final Schema? _patternProps = patternProperties();
if (_patternProps != null) {
// TODO
}
return json;
}
}
class ArraySchema extends Schema {
Schema? _elementSchema;
List<Schema>? _elementsSchemas;
int? minItems;
int? maxItems;
ArraySchema(var jsonSchema, String propertyName)
: super(jsonSchema, propertyName) {
if (jsonSchema["items"] != null) {
_elementSchema =
Fuzzer.self.schemaForArray(jsonSchema["items"], propertyName);
} else if (jsonSchema["prefixItems"] != null) {
_elementsSchemas =
Fuzzer.self.schemasForArray(jsonSchema["prefixItems"], propertyName);
} else {
throw "Expected items/prefixItems specification in array. name=$propertyName , json=${jsonSchema}";
}
minItems = jsonSchema["minItems"];
maxItems = jsonSchema["maxItems"];
}
@override
dynamic generate() {
final int min = minItems ?? 0;
final int max = maxItems ?? (min + 10);
final int numElements = generateNumber(min, max);
var result = [];
for (int i = 0; i < numElements; ++i) {
result.add(elementSchema(i).generate());
}
return result;
}
Schema elementSchema(int indexHint) {
if (_elementSchema != null) return _elementSchema!;
if (indexHint < _elementsSchemas!.length)
return _elementsSchemas![indexHint];
throw "Invalid index=$indexHint for ${_elementsSchemas}";
}
}
class Fuzzer {
final ObjectSchema schema;
late final definitions;
static late Fuzzer self;
Fuzzer(this.schema) {
self = this;
definitions = schema.jsonSchema["definitions"];
assert(schema.isObject());
}
dynamic run() {
dynamic json = {};
_current_children_recursion_depth = 0;
_current_children_max_recursion_depth = generateNumber(
_min_children_recursion_depth, _max_children_recursion_depth);
generate(schema, json);
return json;
}
Schema schemaForArray(var json, String propertyName) {
if (json["\$ref"] != null) {
return schemaForDefinition(json["\$ref"], propertyName);
} else if (json["type"] != null) {
return Schema.fromJson(json, propertyName);
} else {
throw "schemaForArray: Unknown schema for array";
}
}
List<Schema> schemasForArray(var json, String propertyName) {
List<Schema> schemas = [];
for (var element in json) {
final definition = element["\$ref"];
schemas.add(schemaForDefinition(definition, propertyName));
}
return schemas;
}
/// Receives a string like "#/definitions/lastOverlayedGeometry"
Schema schemaForDefinition(String definitionRef, String propertyName) {
final prefix = "#/definitions/";
if (!definitionRef.startsWith(prefix))
throw "Invalid definition ref $definitionRef";
definitionRef = definitionRef.replaceAll(prefix, "");
final json = definitions[definitionRef];
return Schema.fromJson(json, propertyName);
}
void generate(ObjectSchema currentSchema, dynamic generatedJson) {
for (var p in currentSchema.properties()) {
if (p.propertyName.isEmpty) {
throw "Empty prop name";
} else {
generatedJson[p.propertyName] = p.generate();
}
}
}
}
main(List<String> args) {
if (args.length != 1) {
print("Expected layout schema filename");
return;
}
final schemaStr = File(args.first).readAsStringSync();
final jsonSchema = jsonDecode(schemaStr);
final fuzzer = Fuzzer(ObjectSchema(jsonSchema, ""));
final result = fuzzer.run();
JsonEncoder encoder = new JsonEncoder.withIndent(' ');
String prettyprint = encoder.convert(result);
print(prettyprint);
}