abc_parser/
lib.rs

1//! # ABC Parser (WIP)
2//!
3//! An ABC music notation parser for Rust based on the
4//! [2.1 Standard](http://abcnotation.com/wiki/abc:standard:v2.1). This crate is currently a work
5//! in progress and not fully compliant. For full documentation on which parts of the standard
6//! are implemented, take a look at the tests.
7//!
8//! For parsing examples see [abc](abc/index.html) module.
9
10pub mod datatypes;
11mod grammar;
12
13pub use crate::grammar::abc;
14
15#[cfg(test)]
16mod tests {
17    use pretty_assertions::assert_eq;
18
19    use super::*;
20    use crate::datatypes::builder::NoteBuilder;
21    use crate::datatypes::*;
22
23    fn make_tune_header(x: &str, t: &str, k: &str) -> TuneHeader {
24        TuneHeader::new(vec![
25            HeaderLine::Field(InfoField::new('X', x.to_string()), None),
26            HeaderLine::Field(InfoField::new('T', t.to_string()), None),
27            HeaderLine::Field(InfoField::new('K', k.to_string()), None),
28        ])
29    }
30
31    #[test]
32    fn empty_tune_book() {
33        let t = abc::tune_book("").unwrap();
34        assert_eq!(t, TuneBook::new(vec![], None, vec![]));
35    }
36
37    #[test]
38    fn tune_book() {
39        let t = abc::tune_book("X:1\nT:Some Title\nK:G\n").unwrap();
40        assert_eq!(
41            t,
42            TuneBook::new(
43                vec![],
44                None,
45                vec![Tune::new(make_tune_header("1", "Some Title", "G"), None)]
46            )
47        );
48    }
49
50    #[test]
51    fn tune_book_comments() {
52        let t = abc::tune_book("\n\n% A comment\n\n% Some empty lines\nX:1\nT:Some Title\nK:G\n")
53            .unwrap();
54        assert_eq!(
55            t,
56            TuneBook::new(
57                vec![
58                    IgnoredLine::EmptyLine,
59                    IgnoredLine::EmptyLine,
60                    IgnoredLine::Comment(Comment::CommentLine(
61                        "".to_string(),
62                        " A comment".to_string()
63                    )),
64                    IgnoredLine::EmptyLine,
65                    IgnoredLine::Comment(Comment::CommentLine(
66                        "".to_string(),
67                        " Some empty lines".to_string()
68                    )),
69                ],
70                None,
71                vec![Tune::new(make_tune_header("1", "Some Title", "G"), None)]
72            )
73        );
74    }
75
76    #[test]
77    fn tune_book_comments_2() {
78        let t = abc::tune_book("%%%%%%%\n% C1\n% C2\n\n% C3\nX:1\nT:Some Title\nK:G\n").unwrap();
79        assert_eq!(
80            t,
81            TuneBook::new(
82                vec![
83                    IgnoredLine::Comment(Comment::StylesheetDirective("%%%%%".to_string())),
84                    IgnoredLine::Comment(Comment::CommentLine("".to_string(), " C1".to_string())),
85                    IgnoredLine::Comment(Comment::CommentLine("".to_string(), " C2".to_string())),
86                    IgnoredLine::EmptyLine,
87                    IgnoredLine::Comment(Comment::CommentLine("".to_string(), " C3".to_string())),
88                ],
89                None,
90                vec![Tune::new(make_tune_header("1", "Some Title", "G"), None)]
91            )
92        );
93    }
94
95    #[test]
96    fn file_header() {
97        let t = abc::tune_book("O:Some origin info\n\nX:1\nT:Some Title\nK:G\n").unwrap();
98        assert_eq!(
99            t,
100            TuneBook::new(
101                vec![],
102                Some(FileHeader::new(vec![HeaderLine::Field(
103                    InfoField::new('O', "Some origin info".to_string()),
104                    None
105                )]),),
106                vec![Tune::new(make_tune_header("1", "Some Title", "G"), None)]
107            )
108        );
109    }
110
111    #[test]
112    fn file_header_comments() {
113        let t = abc::tune_book(
114            "\
115% Comment 1
116O:Some origin info
117% Comment 2
118
119X:1
120T:Some Title
121K:G
122",
123        )
124        .unwrap();
125        assert_eq!(
126            t,
127            TuneBook::new(
128                vec![IgnoredLine::Comment(Comment::CommentLine(
129                    "".to_string(),
130                    " Comment 1".to_string()
131                ))],
132                Some(FileHeader::new(vec![
133                    HeaderLine::Field(InfoField::new('O', "Some origin info".to_string()), None),
134                    HeaderLine::Comment(Comment::CommentLine(
135                        "".to_string(),
136                        " Comment 2".to_string()
137                    )),
138                ])),
139                vec![Tune::new(make_tune_header("1", "Some Title", "G"), None)]
140            )
141        );
142    }
143
144    #[test]
145    fn file_header_comment_empty() {
146        assert_eq!(
147            abc::file_header("%\n\n").unwrap(),
148            FileHeader::new(vec![HeaderLine::Comment(Comment::CommentLine(
149                "".to_string(),
150                "".to_string()
151            ))])
152        );
153    }
154
155    #[test]
156    fn file_header_comment_mixed() {
157        assert_eq!(
158            abc::file_header("% Some text 123-45[\teu] \n\n").unwrap(),
159            FileHeader::new(vec![HeaderLine::Comment(Comment::CommentLine(
160                "".to_string(),
161                " Some text 123-45[\teu] ".to_string()
162            ))])
163        );
164    }
165
166    #[test]
167    fn file_header_comment_preceding_whitespace() {
168        assert_eq!(
169            abc::file_header("    % Some text\n\n").unwrap(),
170            FileHeader::new(vec![HeaderLine::Comment(Comment::CommentLine(
171                "    ".to_string(),
172                " Some text".to_string()
173            ))])
174        );
175    }
176
177    #[test]
178    fn file_header_trailing_comments() {
179        let file_header = abc::file_header(
180            "\
181O:Origin % Comment
182B:Book%Another comment
183
184",
185        )
186        .unwrap();
187        assert_eq!(
188            file_header,
189            FileHeader::new(vec![
190                HeaderLine::Field(
191                    InfoField::new('O', "Origin ".to_string()),
192                    Some(Comment::Comment(" Comment".to_string()))
193                ),
194                HeaderLine::Field(
195                    InfoField::new('B', "Book".to_string()),
196                    Some(Comment::Comment("Another comment".to_string()))
197                ),
198            ])
199        );
200    }
201
202    #[test]
203    fn file_header_stylesheet_directive_empty() {
204        let text = "%%\n\n";
205        assert_eq!(
206            abc::file_header(text).unwrap(),
207            FileHeader::new(vec![HeaderLine::Comment(Comment::StylesheetDirective(
208                "".to_string()
209            ))])
210        );
211    }
212
213    #[test]
214    fn file_header_stylesheet_directive() {
215        let text = "%%pagewidth\t21cm\n\n";
216        assert_eq!(
217            abc::file_header(text).unwrap(),
218            FileHeader::new(vec![HeaderLine::Comment(Comment::StylesheetDirective(
219                "pagewidth\t21cm".to_string()
220            ))])
221        );
222    }
223
224    #[test]
225    fn invalid_tune() {
226        abc::tune("a").unwrap_err();
227    }
228
229    #[test]
230    fn empty_tune_no_body() {
231        let t = abc::tune("X:1\nT:Some Title\nK:G\n").unwrap();
232        assert_eq!(t, Tune::new(make_tune_header("1", "Some Title", "G"), None));
233    }
234
235    #[test]
236    fn empty_tune_empty_body() {
237        let t = abc::tune("X:1\nT:Some Title\nK:G\n\n").unwrap();
238        assert_eq!(
239            t,
240            Tune::new(
241                make_tune_header("1", "Some Title", "G"),
242                Some(TuneBody::new(vec![]))
243            )
244        );
245    }
246
247    #[test]
248    fn tune_header() {
249        let t = abc::tune_header("X:1\nT:Some Title\nK:G\n").unwrap();
250        assert_eq!(t, make_tune_header("1", "Some Title", "G"));
251    }
252
253    #[test]
254    fn tune_header_unicode() {
255        let t = abc::tune_header("X:1\nT:S©ome ©Title©\nK:G\n").unwrap();
256        assert_eq!(t, make_tune_header("1", "S©ome ©Title©", "G"));
257    }
258
259    #[test]
260    fn tune_header_extra_fields() {
261        let t = abc::tune_header("X:1\nT:Some Title\nO:England\nK:G\n").unwrap();
262        assert_eq!(
263            t,
264            TuneHeader::new(vec![
265                HeaderLine::Field(InfoField::new('X', "1".to_string()), None),
266                HeaderLine::Field(InfoField::new('T', "Some Title".to_string()), None),
267                HeaderLine::Field(InfoField::new('O', "England".to_string()), None),
268                HeaderLine::Field(InfoField::new('K', "G".to_string()), None),
269            ])
270        );
271    }
272
273    #[test]
274    fn tune_header_comments() {
275        let t = abc::tune_header(
276            "\
277% Comment 1
278X:1
279% Comment 2
280T:Some Title
281% Comment 3
282O:England
283% Comment 4
284K:G
285",
286        )
287        .unwrap();
288        assert_eq!(
289            t,
290            TuneHeader::new(vec![
291                HeaderLine::Comment(Comment::CommentLine(
292                    "".to_string(),
293                    " Comment 1".to_string()
294                )),
295                HeaderLine::Field(InfoField::new('X', "1".to_string()), None),
296                HeaderLine::Comment(Comment::CommentLine(
297                    "".to_string(),
298                    " Comment 2".to_string()
299                )),
300                HeaderLine::Field(InfoField::new('T', "Some Title".to_string()), None),
301                HeaderLine::Comment(Comment::CommentLine(
302                    "".to_string(),
303                    " Comment 3".to_string()
304                )),
305                HeaderLine::Field(InfoField::new('O', "England".to_string()), None),
306                HeaderLine::Comment(Comment::CommentLine(
307                    "".to_string(),
308                    " Comment 4".to_string()
309                )),
310                HeaderLine::Field(InfoField::new('K', "G".to_string()), None),
311            ])
312        );
313    }
314
315    #[test]
316    fn tune_header_trailing_comments() {
317        let t = abc::tune_header(
318            "\
319X:1%Comment 1
320T:Some Title%Comment 2
321O:England%Comment 3
322K:G%Comment 4
323",
324        )
325        .unwrap();
326        assert_eq!(
327            t,
328            TuneHeader::new(vec![
329                HeaderLine::Field(
330                    InfoField::new('X', "1".to_string()),
331                    Some(Comment::Comment("Comment 1".to_string()))
332                ),
333                HeaderLine::Field(
334                    InfoField::new('T', "Some Title".to_string()),
335                    Some(Comment::Comment("Comment 2".to_string()))
336                ),
337                HeaderLine::Field(
338                    InfoField::new('O', "England".to_string()),
339                    Some(Comment::Comment("Comment 3".to_string()))
340                ),
341                HeaderLine::Field(
342                    InfoField::new('K', "G".to_string()),
343                    Some(Comment::Comment("Comment 4".to_string()))
344                ),
345            ])
346        );
347    }
348
349    #[test]
350    fn tune_header_comment_preceding_whitespace() {
351        let t = abc::tune_header(
352            "\
353X:1
354T:Some Title
355O:England
356    % Some comment
357K:G
358",
359        )
360        .unwrap();
361        assert_eq!(
362            t,
363            TuneHeader::new(vec![
364                HeaderLine::Field(InfoField::new('X', "1".to_string()), None),
365                HeaderLine::Field(InfoField::new('T', "Some Title".to_string()), None),
366                HeaderLine::Field(InfoField::new('O', "England".to_string()), None),
367                HeaderLine::Comment(Comment::CommentLine(
368                    "    ".to_string(),
369                    " Some comment".to_string()
370                )),
371                HeaderLine::Field(InfoField::new('K', "G".to_string()), None),
372            ])
373        );
374    }
375
376    #[test]
377    fn body_with_music() {
378        let b = abc::tune_body("A").unwrap();
379        assert_eq!(
380            b,
381            TuneBody::new(vec![TuneLine::Music(MusicLine::new(vec![
382                NoteBuilder::new(Note::A).build()
383            ]))])
384        )
385    }
386
387    #[test]
388    fn body_with_music_2() {
389        let b = abc::tune_body("A\n").unwrap();
390        assert_eq!(
391            b,
392            TuneBody::new(vec![TuneLine::Music(MusicLine::new(vec![
393                NoteBuilder::new(Note::A).build()
394            ]))])
395        )
396    }
397
398    #[test]
399    fn tune_line_comment_line_1() {
400        let m = abc::tune_line("% This is a comment\n").unwrap();
401        assert_eq!(
402            m,
403            TuneLine::Comment(Comment::CommentLine(
404                "".to_string(),
405                " This is a comment".to_string()
406            ))
407        )
408    }
409
410    #[test]
411    fn tune_line_comment_line_2() {
412        let m = abc::tune_line("% This is a comment").unwrap();
413        assert_eq!(
414            m,
415            TuneLine::Comment(Comment::CommentLine(
416                "".to_string(),
417                " This is a comment".to_string()
418            ))
419        )
420    }
421
422    #[test]
423    fn tune_line_comment_line_3() {
424        let m = abc::tune_line("    % This is a comment").unwrap();
425        assert_eq!(
426            m,
427            TuneLine::Comment(Comment::CommentLine(
428                "    ".to_string(),
429                " This is a comment".to_string()
430            ))
431        )
432    }
433
434    #[test]
435    fn music_line_empty_1() {
436        abc::music_line("").unwrap_err();
437    }
438
439    #[test]
440    fn music_line_empty_2() {
441        abc::music_line("\n").unwrap_err();
442    }
443
444    #[test]
445    fn music_line_comment_err() {
446        abc::music_line("% This is a comment\n").unwrap_err();
447    }
448
449    #[test]
450    fn music_line_comment_line() {
451        // The parser would never produce this when invoked through a higher
452        // rule like `tune_line`. This would produce a TuneLine::Comment instead.
453        let m = abc::music_line("    % This is a comment").unwrap();
454        assert_eq!(
455            m,
456            MusicLine::new(vec![
457                MusicSymbol::Space("    ".to_string()),
458                MusicSymbol::Comment(Comment::Comment(" This is a comment".to_string()))
459            ])
460        )
461    }
462
463    #[test]
464    fn music_line() {
465        let m = abc::music_line("A\n").unwrap();
466        assert_eq!(m, MusicLine::new(vec![NoteBuilder::new(Note::A).build()]))
467    }
468
469    #[test]
470    fn music_line_2() {
471        let m = abc::music_line("A").unwrap();
472        assert_eq!(m, MusicLine::new(vec![NoteBuilder::new(Note::A).build()]))
473    }
474
475    #[test]
476    fn music_line_multiple_notes() {
477        let m = abc::music_line("AB cd").unwrap();
478        assert_eq!(
479            m,
480            MusicLine::new(vec![
481                NoteBuilder::new(Note::A).build(),
482                NoteBuilder::new(Note::B).build(),
483                MusicSymbol::Space(" ".to_string()),
484                NoteBuilder::new(Note::C).octave(2).build(),
485                NoteBuilder::new(Note::D).octave(2).build(),
486            ])
487        )
488    }
489
490    #[test]
491    fn music_line_trailing_comment() {
492        let m = abc::music_line("AB cd% Inline comment\n").unwrap();
493        assert_eq!(
494            m,
495            MusicLine::new(vec![
496                NoteBuilder::new(Note::A).build(),
497                NoteBuilder::new(Note::B).build(),
498                MusicSymbol::Space(" ".to_string()),
499                NoteBuilder::new(Note::C).octave(2).build(),
500                NoteBuilder::new(Note::D).octave(2).build(),
501                MusicSymbol::Comment(Comment::Comment(" Inline comment".to_string())),
502            ])
503        )
504    }
505
506    #[test]
507    fn music_line_chord() {
508        let m =
509            abc::music_line("( \"^I\" !f! [CEG]- > [CEG] \"^IV\" [F=AC]3/2\"^V\"[GBD]/  H[CEG]2 )")
510                .unwrap();
511        assert_eq!(
512            m,
513            MusicLine::new(vec![
514                MusicSymbol::Slur(Slur::Begin),
515                MusicSymbol::Space(" ".to_string()),
516                MusicSymbol::Annotation(Annotation::new(Some(Placement::Above), "I".to_string())),
517                MusicSymbol::Space(" ".to_string()),
518                MusicSymbol::Decoration(Decoration::Unresolved("f".to_string())),
519                MusicSymbol::Space(" ".to_string()),
520                MusicSymbol::BrokenRhythm {
521                    rhythm: ">".to_string(),
522                    before: vec![
523                        MusicSymbol::Chord {
524                            notes: vec![
525                                NoteBuilder::new(Note::C).build(),
526                                NoteBuilder::new(Note::E).build(),
527                                NoteBuilder::new(Note::G).build(),
528                            ],
529                            length: None,
530                            tie: Some(Tie::Solid),
531                        },
532                        MusicSymbol::Space(" ".to_string())
533                    ],
534                    after: vec![
535                        MusicSymbol::Space(" ".to_string()),
536                        MusicSymbol::Chord {
537                            notes: vec![
538                                NoteBuilder::new(Note::C).build(),
539                                NoteBuilder::new(Note::E).build(),
540                                NoteBuilder::new(Note::G).build(),
541                            ],
542                            length: None,
543                            tie: None,
544                        }
545                    ]
546                },
547                MusicSymbol::Space(" ".to_string()),
548                MusicSymbol::Annotation(Annotation::new(Some(Placement::Above), "IV".to_string())),
549                MusicSymbol::Space(" ".to_string()),
550                MusicSymbol::Chord {
551                    notes: vec![
552                        NoteBuilder::new(Note::F).build(),
553                        NoteBuilder::new(Note::A)
554                            .accidental(Accidental::Natural)
555                            .build(),
556                        NoteBuilder::new(Note::C).build(),
557                    ],
558                    length: Some(Length::new(3.0 / 2.0)),
559                    tie: None,
560                },
561                MusicSymbol::Annotation(Annotation::new(Some(Placement::Above), "V".to_string())),
562                MusicSymbol::Chord {
563                    notes: vec![
564                        NoteBuilder::new(Note::G).build(),
565                        NoteBuilder::new(Note::B).build(),
566                        NoteBuilder::new(Note::D).build(),
567                    ],
568                    length: Some(Length::new(0.5)),
569                    tie: None,
570                },
571                MusicSymbol::Space("  ".to_string()),
572                MusicSymbol::Decoration(Decoration::Fermata),
573                MusicSymbol::Chord {
574                    notes: vec![
575                        NoteBuilder::new(Note::C).build(),
576                        NoteBuilder::new(Note::E).build(),
577                        NoteBuilder::new(Note::G).build(),
578                    ],
579                    length: Some(Length::new(2.0)),
580                    tie: None,
581                },
582                MusicSymbol::Space(" ".to_string()),
583                MusicSymbol::Slur(Slur::End),
584            ])
585        );
586    }
587
588    #[test]
589    fn music_line_beams_1() {
590        let m = abc::music_line("A2``B``C").unwrap();
591        assert_eq!(
592            m,
593            MusicLine::new(vec![
594                NoteBuilder::new(Note::A).length(2.0).build(),
595                MusicSymbol::Beam("``".to_string()),
596                NoteBuilder::new(Note::B).build(),
597                MusicSymbol::Beam("``".to_string()),
598                NoteBuilder::new(Note::C).build(),
599            ])
600        )
601    }
602
603    #[test]
604    fn music_line_beams_2() {
605        let m = abc::music_line("```A2``").unwrap();
606        assert_eq!(
607            m,
608            MusicLine::new(vec![
609                MusicSymbol::Beam("```".to_string()),
610                NoteBuilder::new(Note::A).length(2.0).build(),
611                MusicSymbol::Beam("``".to_string()),
612            ])
613        )
614    }
615
616    #[test]
617    fn symbol_line_err_1() {
618        abc::symbol_line("s: A\n").unwrap_err();
619    }
620    #[test]
621    fn symbol_line_err_2() {
622        abc::symbol_line("s: z\n").unwrap_err();
623    }
624    #[test]
625    fn symbol_line_err_3() {
626        abc::symbol_line("s: [AB]\n").unwrap_err();
627    }
628    #[test]
629    fn symbol_line_err_4() {
630        abc::symbol_line("s: {AB}\n").unwrap_err();
631    }
632    #[test]
633    fn symbol_line_err_5() {
634        abc::symbol_line("s: (3abc\n").unwrap_err();
635    }
636
637    #[test]
638    fn symbol_line_1() {
639        let m = abc::symbol_line("s: |\n").unwrap();
640        assert_eq!(
641            m,
642            SymbolLine::new(vec![
643                SymbolLineSymbol::Space(" ".to_string()),
644                SymbolLineSymbol::SymbolAlignment(SymbolAlignment::Bar),
645            ])
646        )
647    }
648
649    #[test]
650    fn symbol_line_2() {
651        let m = abc::symbol_line("s: \"C\" *  \"Am\" * |\n").unwrap();
652        assert_eq!(
653            m,
654            SymbolLine::new(vec![
655                SymbolLineSymbol::Space(" ".to_string()),
656                SymbolLineSymbol::Annotation(Annotation::new(None, "C".to_string())),
657                SymbolLineSymbol::Space(" ".to_string()),
658                SymbolLineSymbol::SymbolAlignment(SymbolAlignment::Skip),
659                SymbolLineSymbol::Space("  ".to_string()),
660                SymbolLineSymbol::Annotation(Annotation::new(None, "Am".to_string())),
661                SymbolLineSymbol::Space(" ".to_string()),
662                SymbolLineSymbol::SymbolAlignment(SymbolAlignment::Skip),
663                SymbolLineSymbol::Space(" ".to_string()),
664                SymbolLineSymbol::SymbolAlignment(SymbolAlignment::Bar),
665            ])
666        )
667    }
668
669    #[test]
670    fn lyric_line_1() {
671        let m = abc::lyric_line("w: doh\n").unwrap();
672        assert_eq!(
673            m,
674            LyricLine::new(vec![
675                LyricSymbol::Space(" ".to_string()),
676                LyricSymbol::Syllable("doh".to_string()),
677            ])
678        )
679    }
680
681    #[test]
682    fn lyric_line_2() {
683        let m = abc::lyric_line("w: doh re mi fa").unwrap();
684        assert_eq!(
685            m,
686            LyricLine::new(vec![
687                LyricSymbol::Space(" ".to_string()),
688                LyricSymbol::Syllable("doh".to_string()),
689                LyricSymbol::Space(" ".to_string()),
690                LyricSymbol::Syllable("re".to_string()),
691                LyricSymbol::Space(" ".to_string()),
692                LyricSymbol::Syllable("mi".to_string()),
693                LyricSymbol::Space(" ".to_string()),
694                LyricSymbol::Syllable("fa".to_string()),
695            ])
696        )
697    }
698
699    #[test]
700    fn lyric_line_3() {
701        let m = abc::lyric_line("w: syll-a-ble").unwrap();
702        assert_eq!(
703            m,
704            LyricLine::new(vec![
705                LyricSymbol::Space(" ".to_string()),
706                LyricSymbol::Syllable("syll".to_string()),
707                LyricSymbol::SymbolAlignment(SymbolAlignment::Break),
708                LyricSymbol::Syllable("a".to_string()),
709                LyricSymbol::SymbolAlignment(SymbolAlignment::Break),
710                LyricSymbol::Syllable("ble".to_string()),
711            ])
712        )
713    }
714
715    #[test]
716    fn lyric_line_4() {
717        let m = abc::lyric_line("w: Sa-ys my au-l' aul' wan, Will~ye come dar-gle?").unwrap();
718        assert_eq!(
719            m,
720            LyricLine::new(vec![
721                LyricSymbol::Space(" ".to_string()),
722                LyricSymbol::Syllable("Sa".to_string()),
723                LyricSymbol::SymbolAlignment(SymbolAlignment::Break),
724                LyricSymbol::Syllable("ys".to_string()),
725                LyricSymbol::Space(" ".to_string()),
726                LyricSymbol::Syllable("my".to_string()),
727                LyricSymbol::Space(" ".to_string()),
728                LyricSymbol::Syllable("au".to_string()),
729                LyricSymbol::SymbolAlignment(SymbolAlignment::Break),
730                LyricSymbol::Syllable("l'".to_string()),
731                LyricSymbol::Space(" ".to_string()),
732                LyricSymbol::Syllable("aul'".to_string()),
733                LyricSymbol::Space(" ".to_string()),
734                LyricSymbol::Syllable("wan,".to_string()),
735                LyricSymbol::Space(" ".to_string()),
736                LyricSymbol::Syllable("Will".to_string()),
737                LyricSymbol::SymbolAlignment(SymbolAlignment::Space),
738                LyricSymbol::Syllable("ye".to_string()),
739                LyricSymbol::Space(" ".to_string()),
740                LyricSymbol::Syllable("come".to_string()),
741                LyricSymbol::Space(" ".to_string()),
742                LyricSymbol::Syllable("dar".to_string()),
743                LyricSymbol::SymbolAlignment(SymbolAlignment::Break),
744                LyricSymbol::Syllable("gle?".to_string()),
745            ])
746        )
747    }
748
749    #[test]
750    fn note() {
751        let s = abc::music_symbol("A").unwrap();
752        assert_eq!(s, NoteBuilder::new(Note::A).build())
753    }
754
755    macro_rules! assert_note_len {
756        ($input:tt, $expected:expr) => {
757            assert_eq!(
758                abc::music_symbol($input).unwrap(),
759                NoteBuilder::new(Note::A).length($expected).build()
760            )
761        };
762    }
763
764    #[test]
765    fn note_length_1() {
766        assert_note_len!("A3", 3f32);
767    }
768    #[test]
769    fn note_length_2() {
770        assert_note_len!("A9001", 9001f32)
771    }
772    #[test]
773    fn note_length_3() {
774        assert_note_len!("A/2", 0.5)
775    }
776    #[test]
777    fn note_length_4() {
778        assert_note_len!("A/", 0.5)
779    }
780    #[test]
781    fn note_length_5() {
782        assert_note_len!("A/4", 0.25)
783    }
784    #[test]
785    fn note_length_6() {
786        assert_note_len!("A//", 0.25)
787    }
788    #[test]
789    fn note_length_7() {
790        assert_note_len!("A3/2", 3.0 / 2.0)
791    }
792    #[test]
793    fn note_length_8() {
794        assert_note_len!("A6821/962", 6821.0 / 962.0)
795    }
796
797    #[test]
798    fn note_length_invalid_1() {
799        abc::music_symbol("A0").unwrap_err();
800    }
801    #[test]
802    fn note_length_invalid_2() {
803        abc::music_symbol("A-1").unwrap_err();
804    }
805
806    #[test]
807    fn note_invalid_1() {
808        abc::music_symbol("I").unwrap_err();
809    }
810    #[test]
811    fn note_invalid_2() {
812        abc::music_symbol("h").unwrap_err();
813    }
814    #[test]
815    fn note_invalid_3() {
816        abc::music_symbol("Y").unwrap_err();
817    }
818    #[test]
819    fn note_invalid_4() {
820        abc::music_symbol("j").unwrap_err();
821    }
822    #[test]
823    fn note_invalid_5() {
824        abc::music_symbol("m").unwrap_err();
825    }
826    #[test]
827    fn note_invalid_6() {
828        abc::music_symbol("U").unwrap_err();
829    }
830
831    macro_rules! assert_annotation {
832        ($input:tt, $placement:expr, $expected: tt) => {
833            assert_eq!(
834                abc::music_symbol($input).unwrap(),
835                MusicSymbol::Annotation(Annotation::new($placement, $expected.to_string()))
836            )
837        };
838    }
839
840    #[test]
841    fn annotation_1() {
842        assert_annotation!("\"^foo\"", Some(Placement::Above), "foo")
843    }
844    #[test]
845    fn annotation_2() {
846        assert_annotation!("\"_foo\"", Some(Placement::Below), "foo")
847    }
848    #[test]
849    fn annotation_3() {
850        assert_annotation!("\"<foo\"", Some(Placement::Left), "foo")
851    }
852    #[test]
853    fn annotation_4() {
854        assert_annotation!("\">foo\"", Some(Placement::Right), "foo")
855    }
856    #[test]
857    fn annotation_5() {
858        assert_annotation!("\"@foo\"", Some(Placement::Auto), "foo")
859    }
860    #[test]
861    fn annotation_6() {
862        assert_annotation!("\"foo\"", None, "foo")
863    }
864
865    macro_rules! assert_bar {
866        ($bar:tt) => {
867            assert_eq!(
868                abc::music_symbol($bar).unwrap(),
869                MusicSymbol::Bar($bar.to_string(), None)
870            )
871        };
872    }
873
874    #[test]
875    fn bar_1() {
876        assert_bar!("|")
877    }
878    #[test]
879    fn bar_2() {
880        assert_bar!("|]")
881    }
882    #[test]
883    fn bar_3() {
884        assert_bar!("||")
885    }
886    #[test]
887    fn bar_4() {
888        assert_bar!("[|")
889    }
890    #[test]
891    fn bar_5() {
892        assert_bar!("|:")
893    }
894    #[test]
895    fn bar_6() {
896        assert_bar!(":|")
897    }
898    #[test]
899    fn bar_7() {
900        assert_bar!("::")
901    }
902    #[test]
903    fn bar_8() {
904        assert_bar!("[|]")
905    }
906    #[test]
907    fn bar_9() {
908        assert_bar!(".|")
909    }
910    #[test]
911    fn bar_10() {
912        assert_bar!("[|:::")
913    }
914
915    #[test]
916    fn beam_1() {
917        assert_eq!(
918            abc::music_symbol("```").unwrap(),
919            MusicSymbol::Beam("```".to_string())
920        )
921    }
922
923    #[test]
924    fn slur_1() {
925        assert_eq!(
926            abc::music_symbol("(").unwrap(),
927            MusicSymbol::Slur(Slur::Begin)
928        )
929    }
930    #[test]
931    fn slur_2() {
932        assert_eq!(
933            abc::music_symbol(".(").unwrap(),
934            MusicSymbol::Slur(Slur::BeginDotted)
935        )
936    }
937    #[test]
938    fn slur_3() {
939        assert_eq!(
940            abc::music_symbol(")").unwrap(),
941            MusicSymbol::Slur(Slur::End)
942        )
943    }
944
945    macro_rules! assert_dec {
946        ($input:tt, $expected:expr) => {
947            assert_eq!(
948                abc::music_symbol($input).unwrap(),
949                MusicSymbol::Decoration($expected)
950            )
951        };
952    }
953
954    #[test]
955    fn decoration_1() {
956        assert_dec!(".", Decoration::Staccato)
957    }
958    #[test]
959    fn decoration_2() {
960        assert_dec!("~", Decoration::Roll)
961    }
962    #[test]
963    fn decoration_3() {
964        assert_dec!("H", Decoration::Fermata)
965    }
966    #[test]
967    fn decoration_4() {
968        assert_dec!("L", Decoration::Accent)
969    }
970    #[test]
971    fn decoration_5() {
972        assert_dec!("M", Decoration::LowerMordent)
973    }
974    #[test]
975    fn decoration_6() {
976        assert_dec!("O", Decoration::Coda)
977    }
978    #[test]
979    fn decoration_7() {
980        assert_dec!("P", Decoration::UpperMordent)
981    }
982    #[test]
983    fn decoration_8() {
984        assert_dec!("S", Decoration::Segno)
985    }
986    #[test]
987    fn decoration_9() {
988        assert_dec!("T", Decoration::Trill)
989    }
990    #[test]
991    fn decoration_10() {
992        assert_dec!("u", Decoration::UpBow)
993    }
994    #[test]
995    fn decoration_11() {
996        assert_dec!("v", Decoration::DownBow)
997    }
998    #[test]
999    fn decoration_12() {
1000        assert_dec!("!somedec!", Decoration::Unresolved("somedec".to_string()))
1001    }
1002
1003    #[test]
1004    fn decoration_invalid() {
1005        abc::music_symbol("!A").unwrap_err();
1006    }
1007
1008    macro_rules! assert_acc {
1009        ($input:tt, $expected:expr) => {
1010            assert_eq!(
1011                abc::music_symbol($input).unwrap(),
1012                NoteBuilder::new(Note::A).accidental($expected).build()
1013            )
1014        };
1015    }
1016
1017    #[test]
1018    fn accidental_1() {
1019        assert_acc!("^A", Accidental::Sharp)
1020    }
1021    #[test]
1022    fn accidental_2() {
1023        assert_acc!("_A", Accidental::Flat)
1024    }
1025    #[test]
1026    fn accidental_3() {
1027        assert_acc!("=A", Accidental::Natural)
1028    }
1029    #[test]
1030    fn accidental_4() {
1031        assert_acc!("^^A", Accidental::DoubleSharp)
1032    }
1033    #[test]
1034    fn accidental_5() {
1035        assert_acc!("__A", Accidental::DoubleFlat)
1036    }
1037
1038    #[test]
1039    fn accidental_invalid_1() {
1040        abc::music_symbol("^^^A").unwrap_err();
1041    }
1042    #[test]
1043    fn accidental_invalid_2() {
1044        abc::music_symbol("___A").unwrap_err();
1045    }
1046    #[test]
1047    fn accidental_invalid_3() {
1048        abc::music_symbol("_^_A").unwrap_err();
1049    }
1050    #[test]
1051    fn accidental_invalid_4() {
1052        abc::music_symbol("^_A").unwrap_err();
1053    }
1054
1055    macro_rules! assert_oct {
1056        ($input:tt, $expected:expr) => {
1057            assert_eq!(
1058                abc::music_symbol($input).unwrap(),
1059                NoteBuilder::new(Note::A).octave($expected).build()
1060            )
1061        };
1062    }
1063
1064    #[test]
1065    fn octave_1() {
1066        assert_oct!("A,", 0)
1067    }
1068    #[test]
1069    fn octave_2() {
1070        assert_oct!("A'", 2)
1071    }
1072    #[test]
1073    fn octave_3() {
1074        assert_oct!("A,,,", -2)
1075    }
1076    #[test]
1077    fn octave_4() {
1078        assert_oct!("A''''", 5)
1079    }
1080    #[test]
1081    fn octave_5() {
1082        assert_oct!("A,'", 1)
1083    }
1084    #[test]
1085    fn octave_6() {
1086        assert_oct!("A,'',,','", 1)
1087    }
1088
1089    #[test]
1090    fn tie() {
1091        assert_eq!(
1092            abc::music_line("A-A").unwrap(),
1093            MusicLine::new(vec![
1094                NoteBuilder::new(Note::A).tie(Tie::Solid).build(),
1095                NoteBuilder::new(Note::A).build(),
1096            ])
1097        );
1098    }
1099
1100    #[test]
1101    fn tie_across_bars() {
1102        assert_eq!(
1103            abc::music_line("abc-|cba").unwrap(),
1104            MusicLine::new(vec![
1105                NoteBuilder::new(Note::A).octave(2).build(),
1106                NoteBuilder::new(Note::B).octave(2).build(),
1107                NoteBuilder::new(Note::C).octave(2).tie(Tie::Solid).build(),
1108                MusicSymbol::Bar("|".to_string(), None),
1109                NoteBuilder::new(Note::C).octave(2).build(),
1110                NoteBuilder::new(Note::B).octave(2).build(),
1111                NoteBuilder::new(Note::A).octave(2).build(),
1112            ])
1113        );
1114    }
1115
1116    #[test]
1117    fn tie_with_length() {
1118        assert_eq!(
1119            abc::music_line("c4-c4").unwrap(),
1120            MusicLine::new(vec![
1121                NoteBuilder::new(Note::C)
1122                    .octave(2)
1123                    .length(4.0)
1124                    .tie(Tie::Solid)
1125                    .build(),
1126                NoteBuilder::new(Note::C).octave(2).length(4.0).build(),
1127            ])
1128        );
1129    }
1130
1131    #[test]
1132    fn tie_dotted() {
1133        assert_eq!(
1134            abc::music_line("C.-C").unwrap(),
1135            MusicLine::new(vec![
1136                NoteBuilder::new(Note::C).tie(Tie::Dotted).build(),
1137                NoteBuilder::new(Note::C).build(),
1138            ])
1139        );
1140    }
1141
1142    #[test]
1143    fn invalid_tie() {
1144        assert!(abc::music_line("c4 -c4").is_err());
1145        assert!(abc::music_line("abc|-cba").is_err());
1146    }
1147
1148    macro_rules! assert_rst {
1149        ($input:tt, $expected:expr) => {
1150            assert_eq!(
1151                abc::music_symbol($input).unwrap(),
1152                MusicSymbol::Rest($expected)
1153            )
1154        };
1155    }
1156
1157    #[test]
1158    fn rest_1() {
1159        assert_rst!("z", Rest::Note(None))
1160    }
1161    #[test]
1162    fn rest_2() {
1163        assert_rst!("x", Rest::NoteHidden(None))
1164    }
1165    #[test]
1166    fn rest_3() {
1167        assert_rst!("Z", Rest::Measure(None))
1168    }
1169    #[test]
1170    fn rest_4() {
1171        assert_rst!("X", Rest::MeasureHidden(None))
1172    }
1173    #[test]
1174    fn rest_5() {
1175        assert_rst!("z3", Rest::Note(Some(Length::new(3.0))))
1176    }
1177    #[test]
1178    fn rest_6() {
1179        assert_rst!("Z10", Rest::Measure(Some(Length::new(10.0))))
1180    }
1181    #[test]
1182    fn rest_7() {
1183        assert_rst!("x7", Rest::NoteHidden(Some(Length::new(7.0))))
1184    }
1185    #[test]
1186    fn rest_8() {
1187        assert_rst!("X900", Rest::MeasureHidden(Some(Length::new(900.0))))
1188    }
1189
1190    #[test]
1191    fn spacer() {
1192        assert_eq!(abc::music_symbol("y").unwrap(), MusicSymbol::Spacer)
1193    }
1194
1195    #[test]
1196    fn endings_error_1() {
1197        abc::ending("[abc").unwrap_err();
1198    }
1199    #[test]
1200    fn endings_error_2() {
1201        abc::ending("[1-").unwrap_err();
1202    }
1203    #[test]
1204    fn endings_error_3() {
1205        abc::ending("[1,").unwrap_err();
1206    }
1207    #[test]
1208    fn endings_error_4() {
1209        abc::ending("[1-3,").unwrap_err();
1210    }
1211
1212    #[test]
1213    fn endings_general() {
1214        let m = abc::music_line("f|[1 d:|[2 d2 B|]").unwrap();
1215        assert_eq!(
1216            m,
1217            MusicLine::new(vec![
1218                NoteBuilder::new(Note::F).octave(2).build(),
1219                MusicSymbol::Bar("|".to_string(), None),
1220                MusicSymbol::Ending("1".to_string()),
1221                MusicSymbol::Space(" ".to_string()),
1222                NoteBuilder::new(Note::D).octave(2).build(),
1223                MusicSymbol::Bar(":|".to_string(), None),
1224                MusicSymbol::Ending("2".to_string()),
1225                MusicSymbol::Space(" ".to_string()),
1226                NoteBuilder::new(Note::D).octave(2).length(2.0).build(),
1227                MusicSymbol::Space(" ".to_string()),
1228                NoteBuilder::new(Note::B).build(),
1229                MusicSymbol::Bar("|]".to_string(), None)
1230            ])
1231        );
1232    }
1233
1234    #[test]
1235    fn endings_1() {
1236        assert_eq!(
1237            abc::ending("[1").unwrap(),
1238            MusicSymbol::Ending("1".to_string())
1239        )
1240    }
1241    #[test]
1242    fn endings_2() {
1243        assert_eq!(
1244            abc::ending("[2").unwrap(),
1245            MusicSymbol::Ending("2".to_string())
1246        )
1247    }
1248    #[test]
1249    fn endings_3() {
1250        assert_eq!(
1251            abc::ending("[87654").unwrap(),
1252            MusicSymbol::Ending("87654".to_string())
1253        )
1254    }
1255    #[test]
1256    fn endings_4() {
1257        assert_eq!(
1258            abc::ending("[1,3").unwrap(),
1259            MusicSymbol::Ending("1,3".to_string())
1260        )
1261    }
1262    #[test]
1263    fn endings_5() {
1264        assert_eq!(
1265            abc::ending("[1-3").unwrap(),
1266            MusicSymbol::Ending("1-3".to_string())
1267        )
1268    }
1269    #[test]
1270    fn endings_6() {
1271        assert_eq!(
1272            abc::ending("[1,3,5-7").unwrap(),
1273            MusicSymbol::Ending("1,3,5-7".to_string())
1274        )
1275    }
1276    #[test]
1277    fn endings_7() {
1278        assert_eq!(
1279            abc::ending("[1,3,5-7,8-10").unwrap(),
1280            MusicSymbol::Ending("1,3,5-7,8-10".to_string())
1281        )
1282    }
1283
1284    #[test]
1285    fn ending_after_bar() {
1286        let m = abc::music_line("|[1").unwrap();
1287        assert_eq!(
1288            m,
1289            MusicLine::new(vec![
1290                MusicSymbol::Bar("|".to_string(), None),
1291                MusicSymbol::Ending("1".to_string())
1292            ])
1293        )
1294    }
1295
1296    #[test]
1297    fn ending_combined_with_bar() {
1298        let m = abc::music_line("|1").unwrap();
1299        assert_eq!(
1300            m,
1301            MusicLine::new(vec![MusicSymbol::Bar(
1302                "|".to_string(),
1303                Some("1".to_string())
1304            )])
1305        )
1306    }
1307
1308    #[test]
1309    fn chord_1() {
1310        let c = abc::music_symbol("[CEG]").unwrap();
1311        assert_eq!(
1312            c,
1313            MusicSymbol::Chord {
1314                notes: vec![
1315                    NoteBuilder::new(Note::C).build(),
1316                    NoteBuilder::new(Note::E).build(),
1317                    NoteBuilder::new(Note::G).build(),
1318                ],
1319                length: None,
1320                tie: None,
1321            }
1322        );
1323    }
1324
1325    #[test]
1326    fn chord_2() {
1327        let c = abc::music_symbol("[C2E2G2]3").unwrap();
1328        assert_eq!(
1329            c,
1330            MusicSymbol::Chord {
1331                notes: vec![
1332                    NoteBuilder::new(Note::C).length(2.0).build(),
1333                    NoteBuilder::new(Note::E).length(2.0).build(),
1334                    NoteBuilder::new(Note::G).length(2.0).build(),
1335                ],
1336                length: Some(Length::new(3.0)),
1337                tie: None,
1338            }
1339        );
1340    }
1341
1342    #[test]
1343    fn chord_3() {
1344        let c = abc::music_symbol("[!1!C!3!E!5!G]").unwrap();
1345        assert_eq!(
1346            c,
1347            MusicSymbol::Chord {
1348                notes: vec![
1349                    MusicSymbol::Decoration(Decoration::Unresolved("1".to_string())),
1350                    NoteBuilder::new(Note::C).build(),
1351                    MusicSymbol::Decoration(Decoration::Unresolved("3".to_string())),
1352                    NoteBuilder::new(Note::E).build(),
1353                    MusicSymbol::Decoration(Decoration::Unresolved("5".to_string())),
1354                    NoteBuilder::new(Note::G).build(),
1355                ],
1356                length: None,
1357                tie: None,
1358            }
1359        );
1360    }
1361
1362    #[test]
1363    fn grace_notes_empty() {
1364        abc::music_symbol("{}").unwrap_err();
1365    }
1366    #[test]
1367    fn grace_notes_err_1() {
1368        abc::music_symbol("{vA}").unwrap_err();
1369    }
1370    #[test]
1371    fn grace_notes_err_2() {
1372        abc::music_symbol("{HA}").unwrap_err();
1373    }
1374    #[test]
1375    fn grace_notes_err_3() {
1376        abc::music_symbol("{!fermata!A}").unwrap_err();
1377    }
1378
1379    #[test]
1380    fn grace_notes_1() {
1381        let g = abc::music_symbol("{GdGe}").unwrap();
1382        assert_eq!(
1383            g,
1384            MusicSymbol::GraceNotes {
1385                acciaccatura: None,
1386                notes: vec![
1387                    NoteBuilder::new(Note::G).build(),
1388                    NoteBuilder::new(Note::D).octave(2).build(),
1389                    NoteBuilder::new(Note::G).build(),
1390                    NoteBuilder::new(Note::E).octave(2).build()
1391                ]
1392            }
1393        )
1394    }
1395    #[test]
1396    fn grace_notes_2() {
1397        let g = abc::music_symbol("{/g}").unwrap();
1398        assert_eq!(
1399            g,
1400            MusicSymbol::GraceNotes {
1401                acciaccatura: Some(()),
1402                notes: vec![NoteBuilder::new(Note::G).octave(2).build()]
1403            }
1404        )
1405    }
1406    #[test]
1407    fn grace_notes_3() {
1408        let g = abc::music_symbol("{A<B}").unwrap();
1409        assert_eq!(
1410            g,
1411            MusicSymbol::GraceNotes {
1412                acciaccatura: None,
1413                notes: vec![MusicSymbol::BrokenRhythm {
1414                    rhythm: "<".to_string(),
1415                    before: vec![NoteBuilder::new(Note::A).build()],
1416                    after: vec![NoteBuilder::new(Note::B).build()],
1417                }]
1418            }
1419        )
1420    }
1421    #[test]
1422    fn grace_notes_4() {
1423        let g = abc::music_symbol("{A B}").unwrap();
1424        assert_eq!(
1425            g,
1426            MusicSymbol::GraceNotes {
1427                acciaccatura: None,
1428                notes: vec![
1429                    NoteBuilder::new(Note::A).build(),
1430                    MusicSymbol::Space(" ".to_string()),
1431                    NoteBuilder::new(Note::B).build(),
1432                ]
1433            }
1434        )
1435    }
1436
1437    macro_rules! assert_tup {
1438        ($input:tt, $p:expr, $q:expr, $r:expr) => {
1439            assert_eq!(
1440                abc::music_symbol($input).unwrap(),
1441                MusicSymbol::Tuplet {
1442                    p: $p,
1443                    q: $q,
1444                    r: $r,
1445                }
1446            )
1447        };
1448    }
1449
1450    #[test]
1451    fn tuplet_1() {
1452        assert_tup!("(2", 2, None, None)
1453    }
1454    #[test]
1455    fn tuplet_2() {
1456        assert_tup!("(3", 3, None, None)
1457    }
1458    #[test]
1459    fn tuplet_3() {
1460        assert_tup!("(3::", 3, None, None)
1461    }
1462    #[test]
1463    fn tuplet_5() {
1464        assert_tup!("(3:2", 3, Some(2), None)
1465    }
1466    #[test]
1467    fn tuplet_6() {
1468        assert_tup!("(3:2:3", 3, Some(2), Some(3))
1469    }
1470    #[test]
1471    fn tuplet_7() {
1472        assert_tup!("(9", 9, None, None)
1473    }
1474    #[test]
1475    fn tuplet_8() {
1476        assert_eq!(
1477            abc::music_symbol("(3:2:2").unwrap(),
1478            MusicSymbol::Tuplet {
1479                p: 3,
1480                q: Some(2),
1481                r: Some(2),
1482            }
1483        )
1484    }
1485
1486    #[test]
1487    fn broken_rhythm_1() {
1488        assert_eq!(
1489            abc::music_symbol("A<B").unwrap(),
1490            MusicSymbol::BrokenRhythm {
1491                rhythm: "<".to_string(),
1492                before: vec![NoteBuilder::new(Note::A).build()],
1493                after: vec![NoteBuilder::new(Note::B).build()],
1494            }
1495        )
1496    }
1497
1498    #[test]
1499    fn broken_rhythm_2() {
1500        assert_eq!(
1501            abc::music_symbol("A>>>B").unwrap(),
1502            MusicSymbol::BrokenRhythm {
1503                rhythm: ">>>".to_string(),
1504                before: vec![NoteBuilder::new(Note::A).build()],
1505                after: vec![NoteBuilder::new(Note::B).build()],
1506            }
1507        )
1508    }
1509
1510    #[test]
1511    fn broken_rhythm_3() {
1512        assert_eq!(
1513            abc::music_symbol("z<B").unwrap(),
1514            MusicSymbol::BrokenRhythm {
1515                rhythm: "<".to_string(),
1516                before: vec![MusicSymbol::Rest(Rest::Note(None)),],
1517                after: vec![NoteBuilder::new(Note::B).build()],
1518            }
1519        )
1520    }
1521
1522    #[test]
1523    fn broken_rhythm_4() {
1524        assert_eq!(
1525            abc::music_symbol("A < B").unwrap(),
1526            MusicSymbol::BrokenRhythm {
1527                rhythm: "<".to_string(),
1528                before: vec![
1529                    NoteBuilder::new(Note::A).build(),
1530                    MusicSymbol::Space(" ".to_string())
1531                ],
1532                after: vec![
1533                    MusicSymbol::Space(" ".to_string()),
1534                    NoteBuilder::new(Note::B).build()
1535                ],
1536            }
1537        )
1538    }
1539
1540    #[test]
1541    fn broken_rhythm_5() {
1542        assert_eq!(
1543            abc::music_symbol("[AB] < B").unwrap(),
1544            MusicSymbol::BrokenRhythm {
1545                rhythm: "<".to_string(),
1546                before: vec![
1547                    MusicSymbol::Chord {
1548                        notes: vec![
1549                            NoteBuilder::new(Note::A).build(),
1550                            NoteBuilder::new(Note::B).build()
1551                        ],
1552                        length: None,
1553                        tie: None,
1554                    },
1555                    MusicSymbol::Space(" ".to_string()),
1556                ],
1557                after: vec![
1558                    MusicSymbol::Space(" ".to_string()),
1559                    NoteBuilder::new(Note::B).build()
1560                ],
1561            }
1562        )
1563    }
1564
1565    #[ignore]
1566    #[test]
1567    fn broken_rhythm_6() {
1568        assert_eq!(
1569            abc::music_symbol("A{g}<A").unwrap(),
1570            MusicSymbol::BrokenRhythm {
1571                rhythm: "<".to_string(),
1572                before: vec![MusicSymbol::GraceNotes {
1573                    acciaccatura: None,
1574                    notes: vec![NoteBuilder::new(Note::G).octave(2).build()]
1575                }],
1576                after: vec![NoteBuilder::new(Note::B).build()],
1577            }
1578        )
1579    }
1580
1581    #[test]
1582    fn broken_rhythm_recursive() {
1583        assert_eq!(
1584            abc::music_symbol("A<B<C").unwrap(),
1585            MusicSymbol::BrokenRhythm {
1586                rhythm: "<".to_string(),
1587                before: vec![NoteBuilder::new(Note::A).build()],
1588                after: vec![MusicSymbol::BrokenRhythm {
1589                    rhythm: "<".to_string(),
1590                    before: vec![NoteBuilder::new(Note::B).build()],
1591                    after: vec![NoteBuilder::new(Note::C).build()],
1592                }],
1593            }
1594        )
1595    }
1596
1597    #[test]
1598    fn inline_field() {
1599        assert_eq!(
1600            abc::music_symbol("[M:9/8]").unwrap(),
1601            MusicSymbol::InlineField(InfoField('M', "9/8".to_string()), true),
1602        )
1603    }
1604
1605    #[test]
1606    fn inline_field_line() {
1607        assert_eq!(
1608            abc::tune_line("M:9/8\n").unwrap(),
1609            TuneLine::Music(MusicLine::new(vec![MusicSymbol::InlineField(
1610                InfoField('M', "9/8".to_string()),
1611                false
1612            )])),
1613        )
1614    }
1615
1616    #[test]
1617    fn inline_field_err_1() {
1618        abc::music_symbol("[M:9/8\n").unwrap_err();
1619    }
1620    #[test]
1621    fn inline_field_err_2() {
1622        abc::music_symbol("[C:Trad.]").unwrap_err();
1623    }
1624
1625    macro_rules! assert_reserved {
1626        ($input:expr) => {
1627            assert_eq!(
1628                abc::music_symbol($input).unwrap(),
1629                MusicSymbol::Reserved($input.to_string())
1630            )
1631        };
1632    }
1633
1634    #[test]
1635    fn reserved_1() {
1636        assert_reserved!("#");
1637    }
1638    #[test]
1639    fn reserved_2() {
1640        assert_reserved!("*");
1641    }
1642    #[test]
1643    fn reserved_3() {
1644        assert_reserved!(";");
1645    }
1646    #[test]
1647    fn reserved_4() {
1648        assert_reserved!("?");
1649    }
1650    #[test]
1651    fn reserved_5() {
1652        assert_reserved!("@");
1653    }
1654}