twonly-app-dependencies/hashlib/test/random/random_test.dart
2025-12-07 16:10:41 +01:00

429 lines
12 KiB
Dart

// Copyright (c) 2023, Sudipto Chandra
// All rights reserved. Check LICENSE file for details.
// ignore_for_file: no_leading_underscores_for_local_identifiers
import 'dart:isolate';
import 'dart:typed_data';
import 'package:hashlib/random.dart';
import 'package:hashlib/src/random/generators.dart';
import 'package:test/test.dart';
const int _maxInt = 0xFFFFFFFF;
@pragma('vm:entry-point')
void nextSeedIsolate(SendPort port) {
port.send($generateSeed());
}
var testRandom = HashlibRandom.custom(() => _maxInt);
void runFunctionalText(HashlibRandom rand) {
rand.nextInt();
rand.nextBetween(30, 50);
rand.nextBool();
rand.nextByte();
rand.nextBytes(10);
rand.nextDouble();
rand.nextInt();
rand.nextNumbers(10);
rand.nextString(10);
rand.nextWord();
}
void main() {
group('functional tests', () {
test("secure random", () {
runFunctionalText(HashlibRandom(RNG.secure));
});
test("system random", () {
runFunctionalText(HashlibRandom(RNG.system));
});
group("keccak random", () {
test('functions', () {
runFunctionalText(HashlibRandom(RNG.keccak));
});
test('with seed', () {
var rand = HashlibRandom(RNG.keccak, seed: 100);
expect(rand.nextInt(), 3662713900);
});
});
group("sha256 random", () {
test("functions", () {
runFunctionalText(HashlibRandom(RNG.sha256));
});
test('with seed', () {
var rand = HashlibRandom(RNG.sha256, seed: 100);
expect(rand.nextInt(), 3624764737);
});
});
group("sm3 random", () {
test("functions", () {
runFunctionalText(HashlibRandom(RNG.sm3));
});
test('with seed', () {
var rand = HashlibRandom(RNG.sm3, seed: 100);
expect(rand.nextInt(), 874203019);
});
});
group("md5 random", () {
test("functions", () {
runFunctionalText(HashlibRandom(RNG.md5));
});
test('with seed', () {
var rand = HashlibRandom(RNG.md5, seed: 100);
expect(rand.nextInt(), 368434865);
});
});
test("xxh64 random", () {
runFunctionalText(HashlibRandom(RNG.xxh64));
}, tags: ['vm-only']);
});
test('seed generator uniqueness with futures', () async {
final seeds = await Future.wait(List.generate(
1000,
(_) => Future.microtask($generateSeed),
));
expect(seeds.toSet().length, 1000);
});
test('seed generator uniqueness with isolates', () async {
final receiver = ReceivePort();
await Future.wait(List.generate(
100,
(_) => Isolate.spawn(nextSeedIsolate, receiver.sendPort),
));
final seeds = await receiver.take(100).toList();
expect(seeds.toSet().length, 100);
}, tags: ['vm-only'], timeout: Timeout(Duration(minutes: 5)));
test('random bytes length = 0', () {
expect(randomBytes(0), []);
});
test('random bytes length = 1', () {
expect(randomBytes(1).length, 1);
});
test('random bytes length = 100', () {
expect(randomBytes(100).length, 100);
});
test('random numbers length = 0', () {
expect(randomNumbers(0), []);
});
test('random numbers length = 1', () {
expect(randomNumbers(1).length, 1);
});
test('random numbers length = 100', () {
expect(randomNumbers(100).length, 100);
});
test('random numbers value', () {
final result = randomNumbers(10);
expect(result, anyElement(greaterThan(255)));
});
test('fill random bytes', () {
var data = Uint8List(10);
fillRandom(data.buffer);
expect(data, anyElement(isNonZero));
});
test('fill random numbers', () {
var data = Uint32List(10);
fillNumbers(data);
expect(data, anyElement(greaterThan(255)));
});
test('fill test random', () {
int i, c;
for (c = 0; c <= 100; ++c) {
for (i = 0; i + c <= 100; ++i) {
var data = Uint8List(100);
testRandom.fill(data.buffer, i, c);
int s = data.fold<int>(0, (p, e) => p + (e > 0 ? 1 : 0));
expect(s, c, reason: 'fill($i, $c) : $data');
}
}
});
test('next between', () {
var rand = HashlibRandom.secure();
expect(rand.nextBetween(0, 0), 0);
expect(rand.nextBetween(1, 1), 1);
expect(rand.nextBetween(5, 10), lessThanOrEqualTo(10));
expect(rand.nextBetween(10, 5), greaterThanOrEqualTo(5));
expect(rand.nextBetween(-5, -2), lessThan(0));
expect(rand.nextBetween(-5, -15), lessThan(0));
for (int i = 0; i < 100; ++i) {
expect(rand.nextBetween(0, 1), lessThanOrEqualTo(1));
expect(rand.nextBetween(0, 3), lessThanOrEqualTo(3));
expect(rand.nextBetween(0, 10), lessThanOrEqualTo(10));
expect(rand.nextBetween(0, 50), lessThanOrEqualTo(50));
expect(rand.nextBetween(0, 500), lessThanOrEqualTo(500));
expect(rand.nextBetween(0, 85701), lessThanOrEqualTo(85701));
expect(rand.nextBetween(1, _maxInt), greaterThanOrEqualTo(1));
expect(rand.nextBetween(3, _maxInt), greaterThanOrEqualTo(3));
expect(rand.nextBetween(10, _maxInt), greaterThanOrEqualTo(10));
expect(rand.nextBetween(50, _maxInt), greaterThanOrEqualTo(50));
expect(rand.nextBetween(500, _maxInt), greaterThanOrEqualTo(500));
expect(rand.nextBetween(85701, _maxInt), greaterThanOrEqualTo(85701));
}
});
test('random string throws StateError on empty whitelist', () {
expect(
() => randomString(
50,
whitelist: [],
),
throwsStateError);
expect(
() => randomString(
50,
whitelist: [1, 2, 3],
blacklist: [1, 2, 3],
),
throwsStateError);
expect(
() => randomString(
50,
numeric: true,
blacklist: '0123456789'.codeUnits,
),
throwsStateError);
});
group('HashlibRandom.nextString', () {
late HashlibRandom random;
final _lower = 'abcdefghijklmnopqrstuvwxyz'.codeUnits;
final _upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.codeUnits;
final _digits = '0123456789'.codeUnits;
final _controls = [
0, 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,
127
];
final _punctuations = [
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, //
58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96, 123, 124, 125, 126,
];
setUp(() {
// Mock generator with predictable values
final items = [
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, //
75, 76, 77, 78, 79, 80, 81, 82, 83, 84
];
int p = 0;
random = HashlibRandom.custom(() {
p %= items.length;
return items[p++];
});
});
test('should return a string of correct length', () {
final result = random.nextString(10);
expect(result.length, equals(10));
});
test('should contain only ASCII characters by default', () {
final result = random.nextString(10);
for (int i = 0; i < result.length; i++) {
expect(result.codeUnitAt(i), inInclusiveRange(0, 127));
}
});
test('should include only lowercase characters when lower is true', () {
final result = random.nextString(
25,
lower: true,
upper: false,
numeric: false,
controls: false,
punctuations: false,
);
expect(result.codeUnits, everyElement(isIn(_lower)));
});
test('should include uppercase characters when upper is true', () {
final result = random.nextString(
25,
lower: false,
upper: true,
numeric: false,
controls: false,
punctuations: false,
);
expect(result.codeUnits, everyElement(isIn(_upper)));
});
test('should include numeric characters when numeric is true', () {
final result = random.nextString(
25,
lower: false,
upper: false,
numeric: true,
controls: false,
punctuations: false,
);
expect(result.codeUnits, everyElement(isIn(_digits)));
});
test('should include control characters when controls is true', () {
final result = random.nextString(
25,
lower: false,
upper: false,
numeric: false,
controls: true,
punctuations: false,
);
expect(result.codeUnits, everyElement(isIn(_controls)));
});
test('should include punctuation characters when punctuations is true', () {
final result = random.nextString(
25,
lower: false,
upper: false,
numeric: false,
controls: false,
punctuations: true,
);
expect(result.codeUnits, everyElement(isIn(_punctuations)));
});
test('should include multiple (lower, numeric)', () {
final result = random.nextString(
50,
lower: true,
upper: false,
numeric: true,
controls: false,
punctuations: false,
);
final matcher = [..._lower, ..._digits];
expect(result.codeUnits, everyElement(isIn(matcher)));
});
test('should include multiple (upper, numeric)', () {
final result = random.nextString(
50,
lower: false,
upper: true,
numeric: true,
controls: false,
punctuations: false,
);
final matcher = [..._upper, ..._digits];
expect(result.codeUnits, everyElement(isIn(matcher)));
});
test('should include multiple (numeric, controls)', () {
final result = random.nextString(
50,
lower: false,
upper: false,
numeric: true,
controls: true,
punctuations: false,
);
final matcher = [..._controls, ..._digits];
expect(result.codeUnits, everyElement(isIn(matcher)));
});
test('should include multiple (lower, punctuations)', () {
final result = random.nextString(
50,
lower: true,
upper: false,
numeric: false,
controls: false,
punctuations: true,
);
final matcher = [..._lower, ..._punctuations];
expect(result.codeUnits, everyElement(isIn(matcher)));
});
test('should use whitelist if provided', () {
final whitelist = [65, 66, 67]; // A, B, C
final result = random.nextString(10, whitelist: whitelist);
expect(result.codeUnits, everyElement(isIn(whitelist)));
});
test('should remove characters in blacklist', () {
final blacklist = [65, 66, 67]; // A, B, C
final result = random.nextString(10, blacklist: blacklist, lower: true);
expect(result.codeUnits, isNot(anyOf(blacklist)));
});
test('should throw StateError if whitelist is empty', () {
expect(() => random.nextString(10, whitelist: []),
throwsA(isA<StateError>()));
});
test('should return an empty string if length is 0', () {
final result = random.nextString(0);
expect(result, isEmpty);
});
test('should return deterministic output with the same seed', () {
int p = 0, q = 0;
final random1 = HashlibRandom.custom(() => p++);
final random2 = HashlibRandom.custom(() => q++);
final result1 = random1.nextString(10);
final result2 = random2.nextString(10);
expect(result1, equals(result2));
});
});
group('Test \$seedList', () {
test('Test with a normal length list', () {
int seed = 123456789;
var data = Uint8List(64);
$seedList(data, seed);
expect(data, isNot(equals(Uint8List(64))));
});
test('Test with small list', () {
int seed = 123456789;
for (int i = 1; i < 8; ++i) {
var data = Uint8List(i);
$seedList(data, seed);
expect(data, isNot(equals(Uint8List(i))));
}
});
test('Test with uneven list', () {
int seed = 123456789;
for (int i = 1; i < 4; ++i) {
var data = Uint8List(64 + i);
$seedList(data, seed);
expect(data.skip(64), isNot(equals(Uint8List(i))));
}
});
test('Test with same seed', () {
int seed = 123456789;
var data1 = Uint8List(255);
var data2 = Uint8List(255);
$seedList(data1, seed);
$seedList(data2, seed);
expect(data1, equals(data2));
});
test('Test with different seed', () {
int seed1 = 123456789;
int seed2 = 987654321;
var data1 = Uint8List(255);
var data2 = Uint8List(255);
$seedList(data1, seed1);
$seedList(data2, seed2);
expect(data1, isNot(equals(data2)));
});
});
}