1import unittest
2
3import solution
4
5
6class TestSanity(unittest.TestCase):
7 """Check if all of the classes are present."""
8
9 def test_tone(self):
10 self.assertIn('Tone', dir(solution), 'Убеди се, че класът "Tone" е наличен с точно това име.')
11 self.assertTrue(isinstance(solution.Tone, type), 'Убеди се, че "Tone" е клас.')
12
13 def test_interval(self):
14 self.assertIn('Interval', dir(solution), 'Убеди се, че класът "Interval" е наличен с точно това име.')
15 self.assertTrue(isinstance(solution.Interval, type), 'Убеди се, че "Interval" е клас.')
16
17 def test_chord(self):
18 self.assertIn('Chord', dir(solution), 'Убеди се, че класът "Chord" е наличен с точно това име.')
19 self.assertTrue(isinstance(solution.Chord, type), 'Убеди се, че "Chord" е клас.')
20
21
22if __name__ == '__main__':
23 unittest.main()
1import unittest
2
3from solution import *
4
5
6CANNOT_CHORD_THIS_CHORD = "Cannot have a chord made of only 1 unique tone"
7CANNOT_REMOVE_TONE = "Cannot remove tone {0} from chord {1}"
8INVALID_OPERATION = "Invalid operation"
9
10
11class TestBasicToneFunctionality(unittest.TestCase):
12 """Test the basic functionality of the Tone class (operations excluded)."""
13 TONES = "CDEFGAB"
14 SHARPS = (f"{tone}#" for tone in "CDFGA")
15
16 def test_tone_str(self):
17 for tone in self.TONES:
18 self.assertEqual(str(Tone(tone)), tone)
19 for sharp_letter in self.SHARPS:
20 self.assertEqual(str(Tone(sharp_letter)), sharp_letter)
21
22
23class TestBasicIntervalFunctionality(unittest.TestCase):
24 """Test the basic functionality of the Interval class (operations excluded)."""
25 INTERVALS = (
26 "unison",
27 "minor 2nd",
28 "major 2nd",
29 "minor 3rd",
30 "major 3rd",
31 "perfect 4th",
32 "diminished 5th",
33 "perfect 5th",
34 "minor 6th",
35 "major 6th",
36 "minor 7th",
37 "major 7th"
38 )
39
40 def test_interval_str(self):
41 for index, interval in enumerate(self.INTERVALS):
42 self.assertEqual(str(Interval(index)), interval)
43
44 def test_interval_overflow(self):
45 major_7th = Interval(11 + 12**2)
46 self.assertEqual(str(major_7th), "major 7th")
47
48 def test_interval_negative(self):
49 major_7th = Interval(11)
50 minor_2nd = -major_7th
51 self.assertEqual(str(minor_2nd), "minor 2nd")
52
53
54class TestBasicChordFunctionality(unittest.TestCase):
55 """Test the basic functionality of the Chord class (operations excluded)."""
56
57 def test_chord_str(self):
58 a_major_tones = (Tone(tone) for tone in ["A", "C#", "E"])
59 a_major_chord = Chord(*a_major_tones)
60 self.assertEqual(str(a_major_chord), "A-C#-E")
61
62 def test_chord_ordering(self):
63 f_major_seventh_tones = (Tone(tone) for tone in "FCEA")
64 f_sixth_ninth_chord = Chord(*f_major_seventh_tones)
65 self.assertEqual(str(f_sixth_ninth_chord), "F-A-C-E")
66
67 f_sixth_ninth_tones = (Tone(tone) for tone in "FDAG")
68 f_sixth_ninth_chord = Chord(*f_sixth_ninth_tones)
69 self.assertEqual(str(f_sixth_ninth_chord), "F-G-A-D")
70
71 def test_chord_tone_repetition(self):
72 a_minor_tones = (Tone(tone) for tone in "AAAAAAACCCCCCCCCCCCCCEEEEEEEEEEEEEEEEEE")
73 a_minor_chord = Chord(*a_minor_tones)
74 self.assertEqual(str(a_minor_chord), "A-C-E")
75
76 def test_chord_not_enough_tones(self):
77 a_sharp = Tone("A#")
78 with self.assertRaises(TypeError) as err:
79 Chord(a_sharp)
80 self.assertEqual(str(err.exception), CANNOT_CHORD_THIS_CHORD)
81
82 tones = (Tone(tone) for tone in ["A#", "A#", "A#"])
83 with self.assertRaises(TypeError) as err:
84 Chord(*tones)
85 self.assertEqual(str(err.exception), CANNOT_CHORD_THIS_CHORD)
86
87 def test_is_minor(self):
88 a_minor_tones = (Tone(tone) for tone in "AEC")
89 a_minor_chord = Chord(*a_minor_tones)
90 self.assertTrue(a_minor_chord.is_minor())
91
92 a_major_tones = (Tone(tone) for tone in ["A", "C#", "E"])
93 a_major_chord = Chord(*a_major_tones)
94 self.assertFalse(a_major_chord.is_minor())
95
96 a_kind_of_power_chord_tones = (Tone(tone) for tone in ["A", "B", "D", "E"])
97 a_kind_of_power_chord = Chord(*a_kind_of_power_chord_tones)
98 self.assertFalse(a_kind_of_power_chord.is_minor())
99
100 def test_is_major(self):
101 a_minor_tones = (Tone(tone) for tone in "AEC")
102 a_minor_chord = Chord(*a_minor_tones)
103 self.assertFalse(a_minor_chord.is_major())
104
105 a_major_tones = (Tone(tone) for tone in ["A", "C#", "E"])
106 a_major_chord = Chord(*a_major_tones)
107 self.assertTrue(a_major_chord.is_major())
108
109 a_kind_of_power_chord_tones = (Tone(tone) for tone in ["A", "B", "D", "E"])
110 a_kind_of_power_chord = Chord(*a_kind_of_power_chord_tones)
111 self.assertFalse(a_kind_of_power_chord.is_major())
112
113 def test_is_power_chord(self):
114 a_minor_tones = (Tone(tone) for tone in "AEC")
115 a_minor_chord = Chord(*a_minor_tones)
116 self.assertFalse(a_minor_chord.is_power_chord())
117
118 a_major_tones = (Tone(tone) for tone in ["A", "C#", "E"])
119 a_major_chord = Chord(*a_major_tones)
120 self.assertFalse(a_major_chord.is_power_chord())
121
122 a_kind_of_power_chord_tones = (Tone(tone) for tone in ["A", "B", "D", "E"])
123 a_kind_of_power_chord = Chord(*a_kind_of_power_chord_tones)
124 self.assertTrue(a_kind_of_power_chord.is_power_chord())
125
126 def test_transposed(self):
127 d_major_tones = (Tone(tone) for tone in ["D", "F#", "A"])
128 d_major_chord = Chord(*d_major_tones)
129 result_chord = d_major_chord.transposed(Interval(2))
130 self.assertIsInstance(result_chord, Chord)
131 self.assertEqual(str(result_chord), "E-G#-B")
132
133 def test_transposed_negative(self):
134 e_minor_tones = (Tone(tone) for tone in "EGB")
135 e_minor_chord = Chord(*e_minor_tones)
136 result_chord = e_minor_chord.transposed(-Interval(2))
137 self.assertIsInstance(result_chord, Chord)
138 self.assertEqual(str(result_chord), "D-F-A")
139
140 def test_transposed_overflow(self):
141 d_major_tones = (Tone(tone) for tone in ["D", "F#", "A"])
142 d_major_chord = Chord(*d_major_tones)
143 result_chord = d_major_chord.transposed(Interval(8))
144 self.assertIsInstance(result_chord, Chord)
145 self.assertEqual(str(result_chord), "A#-D-F")
146
147 def test_transposed_negative_overflow(self):
148 e_minor_tones = (Tone(tone) for tone in "EGB")
149 e_minor_chord = Chord(*e_minor_tones)
150 result_chord = e_minor_chord.transposed(-Interval(8))
151 self.assertIsInstance(result_chord, Chord)
152 self.assertEqual(str(result_chord), "G#-B-D#")
153
154
155class TestOperations(unittest.TestCase):
156 """Test the operations between the different musical primitives."""
157
158 def test_tone_addition_different_tones(self):
159 g, f = Tone("F"), Tone("G")
160 result_chord = g + f
161 self.assertIsInstance(result_chord, Chord)
162 self.assertEqual(str(result_chord), "F-G")
163
164 def test_tone_addition_same_tone(self):
165 c, another_c = Tone("C"), Tone("C")
166 with self.assertRaises(TypeError) as err:
167 c + another_c
168 self.assertEqual(str(err.exception), CANNOT_CHORD_THIS_CHORD)
169
170 def test_tone_subtraction(self):
171 g, c = Tone("G"), Tone("C")
172 perfect_5th = g - c
173 self.assertEqual(str(perfect_5th), "perfect 5th")
174
175 def test_tone_subtraction_inverse(self):
176 c, g = Tone("C"), Tone("G")
177 perfect_4th = c - g
178 self.assertEqual(str(perfect_4th), "perfect 4th")
179
180 def test_tone_subtraction_same_tone(self):
181 c, another_c = Tone("C"), Tone("C")
182 unison = c - another_c
183 self.assertEqual(str(unison), "unison")
184
185 def test_add_interval_to_tone(self):
186 g = Tone("G")
187 g_sharp = g + Interval(1)
188 self.assertIsInstance(g_sharp, Tone)
189 self.assertEqual(str(g_sharp), "G#")
190
191 def test_add_interval_to_tone_same_tone(self):
192 g = Tone("G")
193 still_g = g + Interval(0)
194 self.assertIsInstance(still_g, Tone)
195 self.assertEqual(str(still_g), "G")
196
197 def test_add_interval_to_tone_overflow(self):
198 g = Tone("G")
199 a = g + Interval(2)
200 self.assertIsInstance(a, Tone)
201 self.assertEqual(str(a), "A")
202
203 a = g + Interval(2 + 12**2)
204 self.assertIsInstance(a, Tone)
205 self.assertEqual(str(a), "A")
206
207 def test_add_interval_to_tone_left_side_error(self):
208 g = Tone("G")
209 with self.assertRaises(TypeError) as err:
210 Interval(2) + g
211 self.assertEqual(str(err.exception), INVALID_OPERATION)
212
213 def test_subtract_interval_from_tone(self):
214 g = Tone("G")
215 f_sharp = g - Interval(1)
216 self.assertIsInstance(f_sharp, Tone)
217 self.assertEqual(str(f_sharp), "F#")
218
219 def test_subtract_interval_from_tone_same_tone(self):
220 g = Tone("G")
221 still_g = g - Interval(0)
222 self.assertIsInstance(still_g, Tone)
223 self.assertEqual(str(still_g), "G")
224
225 def test_subtract_interval_from_tone_oveflow(self):
226 c = Tone("C")
227 g = c - Interval(5)
228 self.assertIsInstance(g, Tone)
229 self.assertEqual(str(g), "G")
230
231 def test_subtract_interval_from_tone_left_side_error(self):
232 g = Tone("G")
233 with self.assertRaises(TypeError) as err:
234 Interval(2) - g
235 self.assertEqual(str(err.exception), INVALID_OPERATION)
236
237 def test_interval_addition(self):
238 minor_2nd, minor_3rd = Interval(1), Interval(3)
239 major_3rd = minor_2nd + minor_3rd
240 self.assertIsInstance(major_3rd, Interval)
241 self.assertEqual(str(major_3rd), "major 3rd")
242
243 def test_interval_addition_overflow(self):
244 perfect_4th, perfect_5th = Interval(5), Interval(7)
245 unison = perfect_4th + perfect_5th
246 self.assertIsInstance(unison, Interval)
247 self.assertEqual(str(unison), "unison")
248
249 def test_add_tone_to_chord(self):
250 f_major_tones = (Tone(tone) for tone in "FAC")
251 f_major_chord = Chord(*f_major_tones)
252 result_chord = f_major_chord + Tone("D")
253 self.assertIsInstance(result_chord, Chord)
254 self.assertEqual(str(result_chord), "F-A-C-D")
255
256 def test_add_tone_to_chord_order(self):
257 f_major_tones = (Tone(tone) for tone in "FAC")
258 f_major_chord = Chord(*f_major_tones)
259 result_chord = f_major_chord + Tone("G")
260 self.assertIsInstance(result_chord, Chord)
261 self.assertEqual(str(result_chord), "F-G-A-C")
262
263 def test_add_tone_to_chord_existing_tone(self):
264 f_minor_tones = (Tone(tone) for tone in ["F", "G#", "C"])
265 f_minor_chord = Chord(*f_minor_tones)
266 result_chord = f_minor_chord + Tone("F")
267 result_chord = result_chord + Tone("G#")
268 result_chord = result_chord + Tone("C")
269 self.assertIsInstance(result_chord, Chord)
270 self.assertEqual(str(result_chord), "F-G#-C")
271
272 def test_subtract_tone_from_chord(self):
273 f_minor_tones = (Tone(tone) for tone in ["F", "G#", "C"])
274 f_minor_chord = Chord(*f_minor_tones)
275 result_chord = f_minor_chord - Tone("C")
276 self.assertIsInstance(result_chord, Chord)
277 self.assertEqual(str(result_chord), "F-G#")
278
279 def test_subtract_tone_from_chord_error(self):
280 c5_chord_tones = Tone("C"), Tone("G")
281 c5_chord = Chord(*c5_chord_tones)
282 with self.assertRaises(TypeError) as err:
283 c5_chord - Tone("G")
284 self.assertEqual(str(err.exception), CANNOT_CHORD_THIS_CHORD)
285
286 def test_subtract_tone_from_chord_error(self):
287 c5_chord_tones = Tone("C"), Tone("G")
288 c5_chord = Chord(*c5_chord_tones)
289 a = Tone("A")
290 expected_message = CANNOT_REMOVE_TONE.format(str(a), str(c5_chord))
291 with self.assertRaises(TypeError) as err:
292 c5_chord - a
293 self.assertEqual(str(err.exception), expected_message)
294
295 def test_add_chords(self):
296 c5_chord = Chord(Tone("C"), Tone("G"))
297 this_other_chord = Chord(Tone("A"), Tone("B"))
298 result_chord = c5_chord + this_other_chord
299 self.assertEqual(str(result_chord), "C-G-A-B")
300
301 def test_add_chords_repeating_notes(self):
302 c5_chord = Chord(Tone("C"), Tone("G"))
303 inverted_c5_chord = Chord(Tone("G"), Tone("C"))
304 result_chord = c5_chord + inverted_c5_chord
305 self.assertEqual(str(result_chord), "C-G")
306
307
308if __name__ == "__main__":
309 unittest.main()
|
Георги Кунчев
01.11.2024 20:28@Даниел_Стефанов, главният тон на първия акорд е водещ, т.е. този, който стои отляво на '+'.
|
Даниел Стефанов
01.11.2024 20:16При събиране на акорди как се определя кой е главния тон на получения акорд. Взимаме главния тон на първия операнд или взимаме този който е по-напред от двата главни тона като поредност?
|
Георги Кунчев
01.11.2024 15:06@Ивайло_Кънчев, тъй като местата са разменени, посоката е различна. Разстоянието при C - G e 5, т.е "perfect 4th".
|
Ивайло Кънчев
01.11.2024 13:58При изваждането на тонове, ако от C извадим G, какъв трябва да е резултатът? Отново ли ще е Perfect 5?
|
Георги Кунчев
31.10.2024 17:18@Даниел_Стефанов, да. Все още нямаме амбиция да позволим множество файлове. Много работа по сайта ще е. В повечето случаи това е достатъчно. Не пишем Java и е ок да имаме много класове в един файл.
|
|
Виктор Бечев
31.10.2024 10:491. Абсолютно си прав, корегираме!
2. Махането на root-а е валидно (било то и от музикална точка безумно) и ще се държи както си го описал - първия тон след основния става новия root.
3. Истината е, че просто не сме мислили да ви караме да имплементирате **и** такъв тип поведение, така че няма да тестваме с това.
|
Павел Петков
31.10.2024 02:11Имам няколко неясни неща по домашното:
1. c_power_chord = Chord(Tone("C"), Tone("G"))
result_chord = c_power_chord - Tone("E")
TypeError: Cannot remove tone C from chord C-G
Предполагам , че е грешно и трябва да е " Cannot remove tone E from chord C-G"
2. Относно махането на тон от акорд, ако се опитаме да премахнем root на акорда валидна операция ли е. Ако е валидна операция би трябвало новият root да стане следващия тон
и всички останали тонове да се сортират спрямо него или поведението е разлчино.
3. Събирането на тон с акорд асоциативно ли е(тоест да прави едно и също без значение позицията на аргументите) или не трябва да имплементираме тон + акорд или
трябва тон + акорд да връща нов акорд, чийто root = тон и tones стават всички тонове на приетия акорд във функцията.
|