Skip to content
Merged
Show file tree
Hide file tree
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
19 changes: 3 additions & 16 deletions generator/lib/src/code_chunks.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,18 @@
import "dart:convert";
import "package:objectbox/src/modelinfo/index.dart";
import "package:objectbox/src/bindings/constants.dart" show OBXPropertyType;
import "package:source_gen/source_gen.dart" show InvalidGenerationSourceError;

class CodeChunks {
// TODO ModelInfo, once per DB
static String modelInfoLoader() => """
Map<int, ModelEntity> _allOBXModelEntities;

void _loadOBXModelEntities() {
_allOBXModelEntities = {};
ModelInfo modelInfo = ModelInfo.fromMap(||MODEL-JSON||);
modelInfo.entities.forEach((e) => _allOBXModelEntities[e.id.uid] = e);
}

ModelEntity _getOBXModelEntity(int entityUid) {
if (_allOBXModelEntities == null) _loadOBXModelEntities();
if (!_allOBXModelEntities.containsKey(entityUid)) {
throw Exception("entity uid missing in objectbox-model.json: \$entityUid");
}
return _allOBXModelEntities[entityUid];
}
""";

static String instanceBuildersReaders(ModelEntity readEntity) {
String name = readEntity.name;
return """
ModelEntity _${name}_OBXModelGetter() {
return _getOBXModelEntity(${readEntity.id.uid});
return ModelEntity.fromMap(${JsonEncoder().convert(readEntity.toMap())});
}

$name _${name}_OBXBuilder(Map<String, dynamic> members) {
Expand Down
3 changes: 0 additions & 3 deletions generator/lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,6 @@ class EntityGenerator extends GeneratorForAnnotation<obx.Entity> {
final modelJson = JsonEncoder.withIndent(" ").convert(allModels.toMap());
await File(ALL_MODELS_JSON).writeAsString(modelJson);

// add model-json to the generated code
ret = ret.replaceFirst('||MODEL-JSON||', modelJson);

readEntity = allModels.findEntityByName(element.name);
if (readEntity == null) return ret;

Expand Down
8 changes: 8 additions & 0 deletions generator/test/cases/multiple_entities/a.dart_testcase
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "package:objectbox/objectbox.dart";
part "a.g.dart";

@Entity(uid: 1)
class A {
@Id(uid: 11)
int id;
}
34 changes: 34 additions & 0 deletions generator/test/cases/multiple_entities/a.g.dart_expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// **************************************************************************
// EntityGenerator
// **************************************************************************

ModelEntity _A_OBXModelGetter() {
return ModelEntity.fromMap({
"id": "1:1",
"lastPropertyId": "1:11",
"name": "A",
"properties": [
{"id": "1:11", "name": "id", "type": 6, "flags": 1}
]
});
}

A _A_OBXBuilder(Map<String, dynamic> members) {
A r = A();
r.id = members["id"];
return r;
}

Map<String, dynamic> _A_OBXReader(A inst) {
Map<String, dynamic> r = {};
r["id"] = inst.id;
return r;
}

const A_OBXDefs =
EntityDefinition<A>(_A_OBXModelGetter, _A_OBXReader, _A_OBXBuilder);

class A_ {
static final id =
QueryIntegerProperty(entityId: 1, propertyId: 1, obxType: 6);
}
8 changes: 8 additions & 0 deletions generator/test/cases/multiple_entities/b.dart_testcase
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "package:objectbox/objectbox.dart";
part "b.g.dart";

@Entity(uid: 2)
class B {
@Id(uid: 21)
int id;
}
34 changes: 34 additions & 0 deletions generator/test/cases/multiple_entities/b.g.dart_expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// **************************************************************************
// EntityGenerator
// **************************************************************************

ModelEntity _B_OBXModelGetter() {
return ModelEntity.fromMap({
"id": "2:2",
"lastPropertyId": "1:21",
"name": "B",
"properties": [
{"id": "1:21", "name": "id", "type": 6, "flags": 1}
]
});
}

B _B_OBXBuilder(Map<String, dynamic> members) {
B r = B();
r.id = members["id"];
return r;
}

Map<String, dynamic> _B_OBXReader(B inst) {
Map<String, dynamic> r = {};
r["id"] = inst.id;
return r;
}

const B_OBXDefs =
EntityDefinition<B>(_B_OBXModelGetter, _B_OBXReader, _B_OBXBuilder);

class B_ {
static final id =
QueryIntegerProperty(entityId: 2, propertyId: 1, obxType: 6);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [
{
"id": "1:1",
"lastPropertyId": "1:11",
"name": "A",
"properties": [
{
"id": "1:11",
"name": "id",
"type": 6,
"flags": 1
}
]
},
{
"id": "2:2",
"lastPropertyId": "1:21",
"name": "B",
"properties": [
{
"id": "1:21",
"name": "id",
"type": 6,
"flags": 1
}
]
}
],
"lastEntityId": "2:2",
"lastIndexId": "0:0",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 5,
"modelVersionParserMinimum": 5,
"retiredEntityUids": [],
"retiredIndexUids": [],
"retiredPropertyUids": [],
"retiredRelationUids": [],
"version": 1
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import "package:objectbox/objectbox.dart";
part "single_entity.g.dart";

/// A dummy annotation to verify the code is generated properly even with annotations unknown to ObjectBox generator.
class TestingUnknownAnnotation {
const TestingUnknownAnnotation();
}

@Entity(uid: 1234)
@TestingUnknownAnnotation()
class SingleEntity {
@Id(uid: 5678)
int id;

@Property(uid: 4321)
@TestingUnknownAnnotation()
String texta;
}
55 changes: 9 additions & 46 deletions generator/test/cases/single_entity/single_entity.g.dart_expected
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,16 @@
// EntityGenerator
// **************************************************************************

Map<int, ModelEntity> _allOBXModelEntities;

void _loadOBXModelEntities() {
_allOBXModelEntities = {};
ModelInfo modelInfo = ModelInfo.fromMap({
"_note1":
"KEEP THIS FILE! Check it into a version control system (VCS) like git.",
"_note2":
"ObjectBox manages crucial IDs for your object model. See docs for details.",
"_note3":
"If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [
{
"id": "1:1234",
"lastPropertyId": "2:4321",
"name": "SingleEntity",
"properties": [
{"id": "1:5678", "name": "id", "type": 6, "flags": 1},
{"id": "2:4321", "name": "texta", "type": 9}
]
}
],
"lastEntityId": "1:1234",
"lastIndexId": "0:0",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 5,
"modelVersionParserMinimum": 5,
"retiredEntityUids": [],
"retiredIndexUids": [],
"retiredPropertyUids": [],
"retiredRelationUids": [],
"version": 1
});
modelInfo.entities.forEach((e) => _allOBXModelEntities[e.id.uid] = e);
}

ModelEntity _getOBXModelEntity(int entityUid) {
if (_allOBXModelEntities == null) _loadOBXModelEntities();
if (!_allOBXModelEntities.containsKey(entityUid)) {
throw Exception("entity uid missing in objectbox-model.json: $entityUid");
}
return _allOBXModelEntities[entityUid];
}

ModelEntity _SingleEntity_OBXModelGetter() {
return _getOBXModelEntity(1234);
return ModelEntity.fromMap({
"id": "1:1234",
"lastPropertyId": "2:4321",
"name": "SingleEntity",
"properties": [
{"id": "1:5678", "name": "id", "type": 6, "flags": 1},
{"id": "2:4321", "name": "texta", "type": 9}
]
});
}

SingleEntity _SingleEntity_OBXBuilder(Map<String, dynamic> members) {
Expand Down
30 changes: 29 additions & 1 deletion generator/test/generator_test.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,42 @@
import "dart:io";
import "package:test/test.dart";
import 'package:glob/glob.dart' show Glob;
import 'package:path/path.dart';

import "helpers.dart";

Map<String, String> getArgs() {
final result = Map<String, String>();

// accept GENERATOR environment variable as a list of arguments, e.g. GENERATOR=update-expected,target:single_entity
final env = Platform.environment['GENERATOR'] ?? "";

env.split(",").forEach((part) {
final kvPair = part.split(":");
if (kvPair.length < 2) {
result[part] = "";
} else {
result[kvPair[0]] = kvPair.sublist(1).join(":"); // join() just in case there were multiple ":"
}
});

return result;
}

void main() async {
group("generator", () {
tearDown(() {
File("objectbox-model.json").deleteSync();
});

testGeneratorOutput("single_entity");
final args = getArgs();
final updateExpected = args["update-expected"] != null;

for (var testCase in Glob("test/cases/*").listSync()) {
final name = basename(testCase.path);
if (args["target"] == null || args["target"] == name) {
testGeneratorOutput(name, updateExpected);
}
}
});
}
59 changes: 36 additions & 23 deletions generator/test/helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import "package:build/src/analyzer/resolver.dart";
import "package:build_resolvers/src/resolver.dart";

class _InMemoryAssetWriter implements AssetWriter {
String output;
Map<AssetId, String> output;

_InMemoryAssetWriter();
_InMemoryAssetWriter() : output = Map<AssetId, String>();

@override
Future writeAsBytes(AssetId id, List<int> bytes) async {
Expand All @@ -23,23 +23,15 @@ class _InMemoryAssetWriter implements AssetWriter {

@override
Future writeAsString(AssetId id, String contents, {Encoding encoding = utf8}) async {
if (output != null) throw Exception("output was set already");
output = contents;
if (output[id] != null) throw Exception("output was set already");
output[id] = contents;
}
}

class _SingleFileAssetReader extends AssetReader {
AssetId id;

_SingleFileAssetReader(this.id) {
if (id.package != "objectbox_generator") {
throw Exception("asset package needs to be 'objectbox_generator', but got '${id.package}'");
}
}

Future<bool> canRead(AssetId id) async => true; //this.id == id;

Stream<AssetId> findAssets(Glob glob, {String package}) => Stream.fromIterable([id]);
Stream<AssetId> findAssets(Glob glob, {String package}) => throw UnimplementedError();

@override
Future<List<int>> readAsBytes(AssetId id) async => utf8.encode(await readAsString(id));
Expand All @@ -59,24 +51,45 @@ class _SingleFileAssetReader extends AssetReader {
}
}

Future<String> _buildGeneratorOutput(String caseName) async {
AssetId assetId = AssetId("objectbox_generator", "test/cases/$caseName/$caseName.dart");
Future<Map<AssetId, String>> _buildGeneratorOutput(String caseName) async {
final entities = List<AssetId>();
for (var entity in Glob("test/cases/$caseName/*.dart_testcase").listSync()) {
final path = entity.path.substring(0, entity.path.length - "_testcase".length);
entities.add(AssetId("objectbox_generator", path));
}

var writer = _InMemoryAssetWriter();
var reader = _SingleFileAssetReader(assetId);
var reader = _SingleFileAssetReader();
Resolvers resolvers = AnalyzerResolvers();

await runBuilder(objectboxModelFactory(BuilderOptions.empty), [assetId], reader, writer, resolvers);
await runBuilder(objectboxModelFactory(BuilderOptions.empty), entities, reader, writer, resolvers);
return writer.output;
}

void testGeneratorOutput(String caseName) {
void checkExpectedContents(String path, String contents, bool updateExpected) async {
final expectedFile = File(path);
final expectedContents = await expectedFile.readAsString();

if (updateExpected) {
if (expectedContents != contents) {
print("Updating $path");
}
await expectedFile.writeAsString(contents);
} else {
expect(contents, equals(expectedContents));
}
}

void testGeneratorOutput(String caseName, bool updateExpected) {
test(caseName, () async {
String built = await _buildGeneratorOutput(caseName);
String expected = await File("test/cases/$caseName/$caseName.g.dart_expected").readAsString();
expect(built, equals(expected));
Map<AssetId, String> built = await _buildGeneratorOutput(caseName);

built.forEach((assetId, generatedCode) async {
final expectedPath = assetId.path.replaceAll(".objectbox_model.g.part", ".g.dart_expected");
checkExpectedContents(expectedPath, generatedCode, updateExpected);
});

String jsonBuilt = await File("objectbox-model.json").readAsString();
String jsonExpected = await File("test/cases/$caseName/objectbox-model.json_expected").readAsString();
expect(jsonBuilt, equals(jsonExpected));
checkExpectedContents("test/cases/$caseName/objectbox-model.json_expected", jsonBuilt, updateExpected);
});
}
Loading