124 lines
3.9 KiB
Dart
124 lines
3.9 KiB
Dart
|
|
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'],
|
||
|
|
],
|
||
|
|
);
|
||
|
|
});
|
||
|
|
}
|