# Solfege - free ear training software
# Copyright (C) 2007 Tom Cato Amundsen
# License is GPL, see file COPYING

import unittest
import time

from src.testlib import I18nSetup
import src.lessonfile
src.lessonfile._test_mode = True
from src.lessonfile import *
from src import dataparser
import mpd
from src import cfg

class TestParser(unittest.TestCase):
    def test_bad_musictype(self):
        s = 'question {\n' \
            'music = music("c e g", "chor")\n' \
            '}'
        p = LessonfileCommon("<STRING>")
        try:
            p.parse_string(s)
        except src.dataparser.WrongArgumentCount, e:
            self.assertEquals('(line 1): question {\n'
                              '(line 2): music = music("c e g", "chor")\n'
                              '                  ^',
                              e.m_nonwrapped_text)

class _Common(unittest.TestCase):
    def setUp(self):
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        header { title = "jojo" }
        question { music = chord("c e g") }
        """)

class TestChord(unittest.TestCase):
    def setUp(self):
        cfg.set_bool('config/override_default_instrument', False)
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        header { random_transpose = no }
        question { music = chord("c e g") }
        """)
        self.p.m_transpose = mpd.MusicalPitch.new_from_notename("d'")
        self.p._idx = 0
    def get_get_mpd_music_string(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_mpd_music_string(self.p),
            r"\staff{ <c e g> }")
        self.p.header.random_transpose = True
        self.assertEquals(self.p.m_questions[0]['music'].get_mpd_music_string(self.p),
            r"\staff\transpose d'{ <c e g> }")
    def test_get_music_as_notename_list(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_music_as_notename_list(self.p), ['c', 'e', 'g'])
        self.p.header.random_transpose = True
        self.p.m_transpose = mpd.MusicalPitch.new_from_notename("d'")
        self.assertEquals(self.p.m_questions[0]['music'].get_music_as_notename_list(self.p), ['d', 'fis', 'a'])
    def test_get_lilypond_code(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_lilypond_code(self.p), "\\score{  \\transpose c' d'{ <c e g> } \\layout {   ragged-last = ##t   \\context { \\Staff \\remove \"Time_signature_engraver\" }  }}")
    def test_get_lilypond_code_first_note(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_lilypond_code_first_note(self.p), r"\transpose c' d'{ c }")
    def test_play(self):
        question = self.p.m_questions[0]
        question['music'].play(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t60/4 p0:0 n48 n52 n55 d1/4 o48 o52 o55")
    def test_play_arpeggio(self):
        question = self.p.m_questions[0]
        question['music'].play_arpeggio(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t180/4 p0:0 n48 d1/4 o48 n52 d1/4 o52 n55 d1/4 o55")
        self.p.header.random_transpose = True
        question['music'].play_arpeggio(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t180/4 p0:0 n50 d1/4 o50 n54 d1/4 o54 n57 d1/4 o57")

class TestMusic(unittest.TestCase):
    def setUp(self):
        cfg.set_bool('config/override_default_instrument', False)
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        header { random_transpose = no }
        question { music = music("\staff{c''}\staff{e'}\staff{c}") }
        """)
        self.p.m_transpose = mpd.MusicalPitch.new_from_notename("d'")
        self.p._idx = 0
    def test_play(self):
        question = self.p.m_questions[0]
        question['music'].play(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(1),
            "t60/4 p0:0 n0:48 n0:64 n0:72 d1/4 o48 o64 o72")
        cfg.set_bool('config/override_default_instrument', True)
        question['music'].play(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(1),
            "t60/4 p0:3 p1:2 p2:1 n0:72 n1:64 n2:48 d1/4 o48 o64 o72")
    def test_play_slowly(self):
        question = self.p.m_questions[0]
        #test.py TestMusic viser at det ikke blir generert riktig instrument
        #for ovelser som progressions-2 (harmonicprogressiondictation)
        question['music'].play_slowly(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(1),
            "t30/4 p0:0 n0:48 n0:64 n0:72 d1/4 o48 o64 o72")
        cfg.set_bool('config/override_default_instrument', True)
        question['music'].play_slowly(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(1),
            "t30/4 p0:3 p1:2 p2:1 n0:72 n1:64 n2:48 d1/4 o48 o64 o72")

class TestVoice(unittest.TestCase):
    def setUp(self):
        cfg.set_bool('config/override_default_instrument', False)
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        header { random_transpose = no }
        question { music = voice("c d e") }
        """)
        self.p.m_transpose = mpd.MusicalPitch.new_from_notename("d'")
        self.p._idx = 0
    def test_get_mpd_music_string(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_mpd_music_string(self.p),
            r"\staff{ c d e }")
        self.p.header.random_transpose = True
        self.assertEquals(self.p.m_questions[0]['music'].get_mpd_music_string(self.p),
            r"\staff\transpose d'{ c d e }")
    def test_get_lilypond_code(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_lilypond_code(self.p), r"\transpose c' d'{ c d e }")
    def test_get_lilypond_code_first_note(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_lilypond_code_first_note(self.p), r"\score{ \new Staff<<   \new Voice\transpose c' d'{ \cadenzaOn c }  \new Voice{ \hideNotes c d e }  >> \layout { ragged-last = ##t } }")
    def test_play(self):
        question = self.p.m_questions[0]
        question['music'].play(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t60/4 p0:0 n48 d1/4 o48 n50 d1/4 o50 n52 d1/4 o52")
    def test_play_slowly(self):
        question = self.p.m_questions[0]
        question['music'].play_slowly(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t30/4 p0:0 n48 d1/4 o48 n50 d1/4 o50 n52 d1/4 o52")

class TestRvoice(unittest.TestCase):
    def setUp(self):
        cfg.set_bool('config/override_default_instrument', False)
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        header { random_transpose = no }
        question { music = rvoice("c' d b c") }
        """)
        self.p.m_transpose = mpd.MusicalPitch.new_from_notename("d'")
        self.p._idx = 0
    def test_get_mpd_music_string(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_mpd_music_string(self.p),
            r"\staff\relative c'{ c d b c }")
        self.p.header.random_transpose = True
        self.assertEquals(self.p.m_questions[0]['music'].get_mpd_music_string(self.p),
            r"\staff\transpose d'\relative c'{ c d b c }")
    def test_play(self):
        question = self.p.m_questions[0]
        question['music'].play(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t60/4 p0:0 n60 d1/4 o60 n62 d1/4 o62 n59 d1/4 o59 n60 d1/4 o60")
    def test_play_slowly(self):
        question = self.p.m_questions[0]
        question['music'].play_slowly(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t30/4 p0:0 n60 d1/4 o60 n62 d1/4 o62 n59 d1/4 o59 n60 d1/4 o60")
    def test_get_lilypond_code(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_lilypond_code(self.p), r"\transpose c' d'\relative c{ c' d b c }")
    def test_get_lilypond_code_first_note(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_lilypond_code_first_note(self.p), r"\score{ \new Staff<<   \new Voice\transpose c' d'\relative c{ \cadenzaOn c' }  \new Voice{ \hideNotes c' d b c }  >>  \layout { ragged-last = ##t } }")

class TestSatb(unittest.TestCase):
    def setUp(self):
        cfg.set_bool('config/override_default_instrument', False)
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        header { random_transpose = no }
        question { music = satb("c''| e'| g | c") }
        question { key = "aes \\major" music = satb("c''|as'|es'|as") }
        question { music = satb("c''| g' e'| g | c") }
        """)
        self.p.m_transpose = mpd.MusicalPitch.new_from_notename("d'")
        self.p._idx = 0
    def test_get_mpd_music_string(self):
        self.assertEquals(self.p.m_questions[0]['music'].get_mpd_music_string(self.p),
            "\\staff{ \\key c \\major\\stemUp <c''> }\n" \
            "\\addvoice{ \\stemDown <e'> }\n" \
            "\\staff{ \\key c \\major\\clef bass \\stemUp <g>}\n" \
            "\\addvoice{ \\stemDown <c>}")
        # FIXME Satb only works if the music object is the current selected
        # question. Can we get around this?
        self.p._idx  = 1
        self.assertEquals(self.p.m_questions[1]['music'].get_mpd_music_string(self.p),
            "\\staff{ \\key aes \\major\\stemUp <c''> }\n" \
            "\\addvoice{ \\stemDown <as'> }\n" \
            "\\staff{ \\key aes \\major\\clef bass \\stemUp <es'>}\n" \
            "\\addvoice{ \\stemDown <as>}")
    def test_play_arpeggio(self):
        question = self.p.m_questions[0]
        question['music'].play_arpeggio(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t60/4 p0:0 n60 d1/4 o60 n52 d1/4 o52 n55 d1/4 o55 n48 d1/4 o48")
        question = self.p.m_questions[2]
        question['music'].play_arpeggio(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t60/4 p0:0 n60 d1/4 o60 n55 d1/4 o55 n52 d1/4 o52 n55 d1/4 o55 n48 d1/4 o48")


class TestRhythm(unittest.TestCase):
    def setUp(self):
        cfg.set_bool('config/override_default_instrument', False)
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        question { music = rhythm("d c8") }
        """)
        self.p._idx = 0
    def test_play(self):
        question = self.p.m_questions[0]
        question['music'].play(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t60/4 P80 d1/4 o80 P37 d1/8 o37")

class TestPercussion(unittest.TestCase):
    def setUp(self):
        cfg.set_bool('config/override_default_instrument', False)
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        question { music = percussion("e16 f g4") }
        """)
        self.p._idx = 0
    def test_play(self):
        question = self.p.m_questions[0]
        question['music'].play(self.p, question)
        self.assertEquals(soundcard.synth.flush_testdata(),
            "t60/4 P52 d1/16 o52 P53 d1/16 o53 P55 d1/4 o55")


class TestWavfile(unittest.TestCase):
    def setUp(self):
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        question { music = wavfile("lesson-files/share/fifth-small-293.33.wav") }
        """)
        self.p._idx = 0
    def test_play(self):
        question = self.p.m_questions[0]
        if cfg.get_bool('testing/may_play_sound'):
            question['music'].play(self.p, question)
            time.sleep(3)
    def test_get_mpd_music_string(self):
        question = self.p.m_questions[0]
        self.assertEquals(question['music'].get_mpd_music_string(self.p),
                          "Wavfile:lesson-files/share/fifth-small-293.33.wav")

class TestMidifile(unittest.TestCase):
    def setUp(self):
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        question { music = midifile("lesson-files/share/fanfare.midi") }
        """)
        self.p._idx = 0
    def test_play(self):
        question = self.p.m_questions[0]
        if cfg.get_bool('testing/may_play_sound'):
            question['music'].play(self.p, question)
            time.sleep(3)
    def test_get_mpd_music_string(self):
        question = self.p.m_questions[0]
        self.assertEquals(question['music'].get_mpd_music_string(self.p),
                          "Midifile:lesson-files/share/fanfare.midi")


class TestCmdline(unittest.TestCase):
    def setUp(self):
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        question { music = cmdline("/usr/bin/play lesson-files/share/fifth-pure-220.00.wav") }
        """)
        self.p._idx = 0
    def test_play(self):
        question = self.p.m_questions[0]
        if cfg.get_bool('testing/may_play_sound'):
            question['music'].play(self.p, question)
            time.sleep(3)

class TestLessonfileHeader(I18nSetup):
    def test_untranslated_variable(self):
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        header { untranslated = "jojo"
           translated = _("major")
        }
        """)
        self.assertEquals(self.p.header['untranslated'], u"jojo")
        self.assertEquals(self.p.header['untranslated'].cval, u"jojo")
        self.assertEquals(self.p.header['translated'], u"dur")
        self.assertEquals(self.p.header['translated'].cval, u"major")
        self.assertEquals(self.p.header.untranslated, u"jojo")
        self.assertEquals(self.p.header.untranslated.cval, u"jojo")
        self.assertEquals(self.p.header.translated, u"dur")
        self.assertEquals(self.p.header.translated.cval, u"major")

class TestLessonfileQuestion(I18nSetup):
    def test_question_name(self):
        self.p = QuestionsLessonfile("<STRING>")
        self.p.parse_string("""
        question { name = "untranslated" }
        question { name = _("major") }
        question { # question has no name
        }
        """)
        self.assertEquals(self.p.m_questions[0].get_name(), u"untranslated")
        self.assertEquals(self.p.m_questions[0].get_name().cval, u"untranslated")
        self.assertEquals(self.p.m_questions[1].get_name(), u"dur")
        self.assertEquals(self.p.m_questions[1].get_name().cval, u"major")
        self.assertEquals(self.p.m_questions[2].get_name(), u"[no name]")
        self.assertEquals(self.p.m_questions[2].get_name().cval, u"[no name]")

class TestLessonfileMisc(unittest.TestCase):
    def test_eq(self):
        c = Chord("a b c")
        d = Rvoice("a b c")
        e = Rvoice("a b c")
        f = Rvoice("c c c")
        self.assertNotEquals(c, d)
        self.assertNotEquals(d, e)
        self.assertNotEquals(d, f)

suite = unittest.makeSuite(TestParser)
suite.addTest(unittest.makeSuite(TestChord))
suite.addTest(unittest.makeSuite(TestMusic))
suite.addTest(unittest.makeSuite(TestVoice))
suite.addTest(unittest.makeSuite(TestRvoice))
suite.addTest(unittest.makeSuite(TestSatb))
suite.addTest(unittest.makeSuite(TestRhythm))
suite.addTest(unittest.makeSuite(TestPercussion))
suite.addTest(unittest.makeSuite(TestWavfile))
suite.addTest(unittest.makeSuite(TestMidifile))
suite.addTest(unittest.makeSuite(TestCmdline))
suite.addTest(unittest.makeSuite(TestLessonfileHeader))
suite.addTest(unittest.makeSuite(TestLessonfileQuestion))
suite.addTest(unittest.makeSuite(TestLessonfileMisc))
