halkyon/halkyon_learn_curator/test/jmdict_xml_test.dart

124 lines
3.9 KiB
Dart
Raw Normal View History

import 'dart:convert';
import 'dart:io';
import 'package:halkyon_learn_curator/src/sources/jlpt.dart';
import 'package:halkyon_learn_curator/src/sources/jmdict_xml.dart';
import 'package:test/test.dart';
import 'package:xml/xml.dart';
/// [text] as a stream of pieces [size] characters long.
Stream<String> pieces(String text, int size) => Stream.fromIterable([
for (var at = 0; at < text.length; at += size)
text.substring(at, (at + size).clamp(0, text.length)),
]);
void main() {
late String xml;
late List<Map<String, Object?>> lines;
setUpAll(() async {
xml = await File('test/fixtures/jmdict-e-examp-sample.xml').readAsString();
final jlpt = await readJlptLevels(Directory('test/fixtures/jlpt'));
lines = [
for (final line in await jmdictXmlDictionary(
Stream.value(xml),
jlpt: jlpt,
examples: true,
).toList())
jsonDecode(line) as Map<String, Object?>,
];
});
Map<String, Object?> entry(String id) =>
lines.skip(1).firstWhere((line) => line['id'] == id);
List<Map<String, Object?>> senses(String id) => [
for (final sense in (entry(id)['entry']! as Map)['senses'] as List)
sense as Map<String, Object?>,
];
test("the header names EDRDG's JMdict and the day it was made, and "
'credits the sentences and the levels', () {
final header = lines.first;
expect(header['source'], 'jmdict');
expect(header['kind'], 'words');
expect(header['sourceVersion'], '2026-10-09');
expect(
[
for (final credit in header['credits']! as List)
(credit as Map)['name'],
],
['JMdict', 'JLPT vocabulary lists', 'Tatoeba'],
);
expect(lines, hasLength(6));
});
test('a word as from the JSON: its headword and reading, labels, keys, '
"and its senses with JMdict's codes as codes", () {
final taberu = entry('1358280');
expect((taberu['headword'], taberu['reading']), ('食べる', 'たべる'));
expect(taberu['labels'], ['common', 'jlpt:n5']);
expect(taberu['keys'], containsAll(['食べる', 'たべる', 'taberu', 'eat']));
expect(senses('1358280').first['pos'], ['v1', 'vt']);
});
test("a sense's sentences from Tatoeba, the word as it is in each", () {
expect((senses('1358280').first['examples']! as List).first, {
'text': '食べる',
'ja': 'もっと果物を食べるべきです。',
'en': 'You should eat more fruit.',
'tatoeba': '193344',
});
});
test('a word usually written in kana has a kana headword; a sense kept to '
'one reading says so', () {
final sono = entry('1006830');
expect(sono['headword'], 'その');
expect(senses('1006830').first['misc'], ['uk']);
expect(senses('1006830').last['readings'], ['その']);
});
test('the reading is the one restricted to the headword', () {
expect(entry('1000110')['reading'], 'シーディープレーヤー');
});
test(
'entities cut between two pieces of the stream are read whole',
() async {
final again = [
for (final line in await jmdictXmlDictionary(
pieces(xml, 7),
examples: true,
).toList())
jsonDecode(line) as Map<String, Object?>,
];
expect(
[for (final line in again.skip(1)) line['entry']],
[for (final line in lines.skip(1)) line['entry']],
);
},
);
test('a sense without parts of speech has those of the sense before', () {
final word = jmdictWordOf(
XmlDocument.parse('''
<entry><ent_seq>1</ent_seq><r_ele><reb>あ</reb></r_ele>
<sense><pos>n</pos><gloss>one</gloss></sense>
<sense><gloss>two</gloss></sense>
<sense><pos>adv</pos><gloss>three</gloss></sense></entry>''').rootElement,
);
expect(
[
for (final sense in word['sense']! as List)
(sense as Map)['partOfSpeech'],
],
[
['n'],
['n'],
['adv'],
],
);
});
}