Initial flame_lua_runtime package

This commit is contained in:
gem
2026-06-07 22:53:58 +08:00
commit 733b2fb798
262 changed files with 28439 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
import 'package:flame_lua_runtime/runtime/lifecycle/runtime_async_gate.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('RuntimeAsyncGate', () {
test('accepts only current open generation tokens', () {
final gate = RuntimeAsyncGate();
final first = gate.activate();
expect(gate.accepts(first), isTrue);
expect(first.isAccepted, isTrue);
final second = gate.advance();
expect(gate.accepts(first), isFalse);
expect(gate.accepts(second), isTrue);
});
test('close rejects existing and future checks until activated again', () {
final gate = RuntimeAsyncGate();
final first = gate.activate();
gate.close();
expect(gate.isClosed, isTrue);
expect(gate.accepts(first), isFalse);
expect(gate.acceptsGeneration(gate.generation), isFalse);
final second = gate.activate();
expect(gate.isOpen, isTrue);
expect(gate.accepts(second), isTrue);
expect(gate.accepts(first), isFalse);
});
});
}

View File

@@ -0,0 +1,59 @@
import 'package:flame_lua_runtime/runtime/lifecycle/runtime_serial_queue.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('RuntimeSerialQueue', () {
test('drains queued items in order on a microtask', () async {
final handled = <int>[];
final queue = RuntimeSerialQueue<int>(onItem: handled.add);
queue
..enqueue(1)
..enqueue(2)
..enqueue(3);
expect(handled, isEmpty);
expect(queue.pendingCount, 3);
await Future<void>.delayed(Duration.zero);
expect(handled, [1, 2, 3]);
expect(queue.pendingCount, 0);
});
test('stops draining when shouldContinue turns false', () async {
final handled = <int>[];
var active = true;
late final RuntimeSerialQueue<int> queue;
queue = RuntimeSerialQueue<int>(
shouldContinue: () => active,
onItem: (item) {
handled.add(item);
active = false;
},
);
queue
..enqueue(1)
..enqueue(2);
await Future<void>.delayed(Duration.zero);
expect(handled, [1]);
expect(queue.pendingCount, 1);
});
test('dispose drops pending items', () async {
final handled = <int>[];
final queue = RuntimeSerialQueue<int>(onItem: handled.add);
queue.enqueue(1);
queue.dispose();
await Future<void>.delayed(Duration.zero);
expect(handled, isEmpty);
expect(queue.pendingCount, 0);
});
});
}

View File

@@ -0,0 +1,38 @@
import 'package:flame_lua_runtime/runtime/lifecycle/runtime_session.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('RuntimeSession', () {
test('moves through loading, active, disposing and disposed states', () {
final session = RuntimeSession(gameId: 'game');
expect(session.state, RuntimeSessionState.created);
expect(session.acceptsWork, isTrue);
expect(session.isActive, isFalse);
session.beginLoading();
expect(session.state, RuntimeSessionState.loading);
expect(session.acceptsWorkFor(session.id), isTrue);
expect(session.accepts(session.id), isFalse);
session.activate();
expect(session.state, RuntimeSessionState.active);
expect(session.accepts(session.id), isTrue);
session.beginDisposing();
expect(session.state, RuntimeSessionState.disposing);
expect(session.acceptsWork, isFalse);
expect(session.accepts(session.id), isFalse);
session.dispose();
expect(session.state, RuntimeSessionState.disposed);
expect(session.acceptsWork, isFalse);
});
test('rejects invalid transitions', () {
final session = RuntimeSession(gameId: 'game')..activate();
expect(session.beginLoading, throwsA(isA<StateError>()));
});
});
}

View File

@@ -0,0 +1,61 @@
import 'dart:async' as async;
import 'package:flame_lua_runtime/runtime/lifecycle/runtime_task_registry.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('RuntimeTaskRegistry', () {
test('cancels tasks by scope', () async {
final registry = RuntimeTaskRegistry<String>(cancelledValue: 'cancelled');
final scoped = registry.create(scope: 'panel');
final other = registry.create(scope: 'other');
registry.cancelScope('panel');
expect(await scoped.future, 'cancelled');
expect(scoped.isCancelled, isTrue);
expect(other.isCancelled, isFalse);
expect(registry.scopedTaskCount('panel'), 0);
expect(registry.scopedTaskCount('other'), 1);
other.complete('done');
expect(await other.future, 'done');
expect(registry.activeTaskCount, 0);
});
test('cancel runs callbacks and cancels timers', () async {
final registry = RuntimeTaskRegistry<String>(cancelledValue: 'cancelled');
final task = registry.create(scope: 'panel');
var callbackCalled = false;
var timerFired = false;
final timer = async.Timer(const Duration(milliseconds: 30), () {
timerFired = true;
});
task
..addTimer(timer)
..addCancelCallback(() {
callbackCalled = true;
})
..cancel();
await Future<void>.delayed(const Duration(milliseconds: 40));
expect(await task.future, 'cancelled');
expect(callbackCalled, isTrue);
expect(timerFired, isFalse);
});
test('dispose cancels all active tasks', () async {
final registry = RuntimeTaskRegistry<String>(cancelledValue: 'cancelled');
final first = registry.create();
final second = registry.create(scope: 'panel');
registry.dispose();
expect(await first.future, 'cancelled');
expect(await second.future, 'cancelled');
expect(registry.activeTaskCount, 0);
});
});
}