Files
jadb/test/models/word_search_test.dart
T
2026-06-03 17:01:53 +09:00

283 lines
8.2 KiB
Dart

import 'dart:convert';
import 'package:jadb/models/common/jlpt_level.dart';
import 'package:jadb/models/jmdict/jmdict_dialect.dart';
import 'package:jadb/models/jmdict/jmdict_field.dart';
import 'package:jadb/models/jmdict/jmdict_kanji_info.dart';
import 'package:jadb/models/jmdict/jmdict_misc.dart';
import 'package:jadb/models/jmdict/jmdict_pos.dart';
import 'package:jadb/models/jmdict/jmdict_reading_info.dart';
import 'package:jadb/models/word_search/word_search_match_span.dart';
import 'package:jadb/models/word_search/word_search_result.dart';
import 'package:jadb/models/word_search/word_search_ruby.dart';
import 'package:jadb/models/word_search/word_search_sense.dart';
import 'package:jadb/models/word_search/word_search_sense_language_source.dart';
import 'package:jadb/models/word_search/word_search_sources.dart';
import 'package:jadb/models/word_search/word_search_xref_entry.dart';
import 'package:test/test.dart';
Object? _roundTripJson(Object? value) => jsonDecode(jsonEncode(value));
Map<String, dynamic> _roundTripMap(Object? json) =>
Map<String, dynamic>.from(_roundTripJson(json) as Map);
Map<String, Object?> _roundTripObjectMap(Object? json) =>
Map<String, Object?>.from(_roundTripJson(json) as Map);
void _expectScalarRoundTrip<T>({
required Iterable<T> values,
required Object? Function(T value) toJson,
required T Function(Object? json) fromJson,
}) {
for (final value in values) {
expect(
fromJson(_roundTripJson(toJson(value))),
equals(value),
reason: 'Roundtrip failed for $value',
);
}
}
void _expectMapRoundTrip<T>({
required Iterable<T> values,
required Map<String, Object?> Function(T value) toJson,
required T Function(Map<String, Object?> json) fromJson,
}) {
for (final value in values) {
expect(
fromJson(_roundTripObjectMap(toJson(value))),
equals(value),
reason: 'Roundtrip failed for $value',
);
}
}
WordSearchResult _buildNestedXrefResult() => WordSearchResult(
score: 7,
entryId: 300,
isCommon: false,
japanese: [WordSearchRuby(base: '補助', furigana: 'ほじょ')],
kanjiInfo: const {},
readingInfo: const {},
senses: const [],
jlptLevel: JlptLevel.none,
sources: const WordSearchSources(jmdict: true, jmnedict: false),
);
WordSearchSense _buildSense() => WordSearchSense(
englishDefinitions: ['kana', 'syllabary'],
partsOfSpeech: [JMdictPOS.n],
seeAlso: [
WordSearchXrefEntry(
entryId: 300,
ambiguous: false,
baseWord: '仮名遣い',
furigana: 'かなづかい',
xrefResult: _buildNestedXrefResult(),
),
],
antonyms: const [
WordSearchXrefEntry(
entryId: 301,
ambiguous: true,
baseWord: '漢字',
furigana: 'かんじ',
xrefResult: null,
),
],
restrictedToReading: ['かな'],
restrictedToKanji: ['仮名'],
fields: [JMdictField.linguistics],
dialects: [JMdictDialect.kansai],
misc: [JMdictMisc.onlyKana, JMdictMisc.rare],
info: ['Typically written using kana alone.'],
languageSource: const [
WordSearchSenseLanguageSource(
language: 'por',
phrase: 'canoa',
fullyDescribesSense: false,
constructedFromSmallerWords: true,
),
],
);
WordSearchResult _buildWordSearchResult({
List<WordSearchMatchSpan>? matchSpans,
}) => WordSearchResult(
score: 42,
entryId: 123,
isCommon: true,
japanese: [
WordSearchRuby(base: '仮名', furigana: 'かな'),
WordSearchRuby(base: 'かな'),
],
kanjiInfo: {'仮名': JMdictKanjiInfo.rK, '': JMdictKanjiInfo.ateji},
readingInfo: {'かな': JMdictReadingInfo.gikun, 'カナ': JMdictReadingInfo.rk},
senses: [_buildSense()],
jlptLevel: JlptLevel.n5,
sources: const WordSearchSources(jmdict: true, jmnedict: true),
matchSpans: matchSpans,
);
void main() {
test('JlptLevel JSON serialization roundtrip', () {
_expectScalarRoundTrip<JlptLevel>(
values: JlptLevel.values,
toJson: (value) => value.toJson(),
fromJson: JlptLevel.fromJson,
);
});
test('JMdictDialect JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictDialect>(
values: JMdictDialect.values,
toJson: (value) => value.toJson(),
fromJson: JMdictDialect.fromJson,
);
});
test('JMdictField JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictField>(
values: JMdictField.values,
toJson: (value) => value.toJson(),
fromJson: JMdictField.fromJson,
);
});
test('JMdictKanjiInfo JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictKanjiInfo>(
values: JMdictKanjiInfo.values,
toJson: (value) => value.toJson(),
fromJson: JMdictKanjiInfo.fromJson,
);
});
test('JMdictMisc JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictMisc>(
values: JMdictMisc.values,
toJson: (value) => value.toJson(),
fromJson: JMdictMisc.fromJson,
);
});
test('JMdictPOS JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictPOS>(
values: JMdictPOS.values,
toJson: (value) => value.toJson(),
fromJson: JMdictPOS.fromJson,
);
});
test('JMdictReadingInfo JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictReadingInfo>(
values: JMdictReadingInfo.values,
toJson: (value) => value.toJson(),
fromJson: JMdictReadingInfo.fromJson,
);
});
test('WordSearchMatchSpan JSON serialization roundtrip', () {
final span = WordSearchMatchSpan(
spanType: WordSearchMatchSpanType.sense,
index: 2,
subIndex: 3,
start: 4,
end: 8,
);
final restored = WordSearchMatchSpan.fromJson(_roundTripMap(span.toJson()));
expect(restored, equals(span));
expect(restored.subIndex, equals(span.subIndex));
});
test('WordSearchRuby JSON serialization roundtrip', () {
final ruby = WordSearchRuby(base: '仮名', furigana: 'かな');
final restored = WordSearchRuby.fromJson(_roundTripMap(ruby.toJson()));
expect(restored.toJson(), equals(ruby.toJson()));
});
test('WordSearchSenseLanguageSource JSON serialization roundtrip', () {
const source = WordSearchSenseLanguageSource(
language: 'por',
phrase: 'canoa',
fullyDescribesSense: false,
constructedFromSmallerWords: true,
);
final restored = WordSearchSenseLanguageSource.fromJson(
_roundTripMap(source.toJson()),
);
expect(restored.toJson(), equals(source.toJson()));
});
test('WordSearchSources JSON serialization roundtrip', () {
const sources = WordSearchSources(jmdict: false, jmnedict: true);
final restored = WordSearchSources.fromJson(
_roundTripMap(sources.toJson()),
);
expect(restored.toJson(), equals(sources.toJson()));
expect(restored.sqlValue, equals(sources.sqlValue));
});
test('WordSearchXrefEntry JSON serialization roundtrip', () {
final entry = WordSearchXrefEntry(
entryId: 300,
ambiguous: false,
baseWord: '仮名遣い',
furigana: 'かなづかい',
xrefResult: _buildNestedXrefResult(),
);
final restored = WordSearchXrefEntry.fromJson(
_roundTripMap(entry.toJson()),
);
expect(restored.toJson(), equals(entry.toJson()));
expect(restored.xrefResult?.toJson(), equals(entry.xrefResult?.toJson()));
});
test('WordSearchSense JSON serialization roundtrip', () {
final sense = _buildSense();
final restored = WordSearchSense.fromJson(_roundTripMap(sense.toJson()));
expect(restored.toJson(), equals(sense.toJson()));
});
test('WordSearchResult JSON serialization roundtrip', () {
final result = _buildWordSearchResult();
final restored = WordSearchResult.fromJson(_roundTripMap(result.toJson()));
expect(restored.toJson(), equals(result.toJson()));
expect(restored.matchSpans, isNull);
});
test('WordSearchResult leaves matchSpans out of JSON', () {
final result = _buildWordSearchResult(
matchSpans: [
WordSearchMatchSpan(
spanType: WordSearchMatchSpanType.sense,
index: 0,
subIndex: 1,
start: 0,
end: 4,
),
],
);
final json = _roundTripMap(result.toJson());
final restored = WordSearchResult.fromJson(json);
expect(json.containsKey('matchSpans'), isFalse);
expect(restored.matchSpans, isNull);
expect(restored.toJson(), equals(result.toJson()));
});
}