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()
|
Георги Кунчев
06.11.2024 17:31Привет!
Поради големия брой решения, влезли в последните няколко часа, ще довършим проверката и ще публикуваме резултатите утре.
Не се проверяват лесно 60 решения с по 150 реда (9000 реда код) :tired_face:
|
Георги Кунчев
04.11.2024 14:59@Даниел_Стефанов, както Виктор каза, не сме говорили още за тестове, но тъй като пишеш, а ми направи впечатление - ще споделя нещо за твое удобство.
Вместо да закоментираш тест, който не ти трябва за момента, можеш да го декорираш с декоратор, който `unittest` дава наготово:
```
@unittest.skip('No need to test this right now.')
def test_something(self):
self.assertTrue('something')
```
|
Виктор Бечев
04.11.2024 14:55Супер е, че си написал тестове и тестовете ти реално тестват немалко неща.
Проблемът, обаче, е, че са зависими от твоята собствена имплементация. Ето пример:
```
for index, tone in enumerate(c_minor_chord.tones):
self.assertEqual(str(tone), str(expected_tones[index]))
```
Ако колега, на когото имплементацията му не разчита на `Chord.tones` - атрибутът се казва по различен начин или пък изобщо не използва такъв атрибут - тестът фейлва.
Същото важи и за `Interval.length` и прочие.
Освен това имаш дефинирани тестове и за поведение, което не изискваме от вас - например:
```
def test_add_with_invalid_second_param_bool(self):
with self.assertRaises(TypeError) as context:
self.c + True
self.assertIn(INVALID_OPERATION_ERROR_MESSAGE, str(context.exception))
```
Точката е поощрителна, защото още не сме говорили за тестове, а си се постарал, а вече по-натам - ще поощряваме само валидни такива.
Но е важно за всички останали да знаят, че ако някои от тестовете ти не минават - **това не е непременно проблем с тяхното решение**.
|
Даниел Стефанов
04.11.2024 14:33Още тестове: https://pastebin.com/cjMUqPpV фикснете импорта и сменете HOMEWORK_FILENAME във файла
|
Виктор Бечев
03.11.2024 22:07След дискусия в едно от решенията, едно уточнение (новият текст в **bold**):
**Забележка:** Искаме това да работи **само** когато тонът е отляво **на знакът за събиране**. Когато тонът е отдясно, го приемаме за недефинирано поведение и искаме да се възбуди `TypeError` с текст `"Invalid operation"`. Семпло, но няма да ви вгорчаваме живота... Повече.
Говорим за отляво или отдясно спрямо операцията, а не някаква релативност спрямо скалата.
|
Виктор Бечев
03.11.2024 21:21@Стефан_Шиваров - реално това ще работи без да правиш нищо. Не защото ще е "static", а защото това е разликата между функция и bound метод. `Chord.is_minor(chord)` ще предаде експлицитно `chord` като `self` на `is_minor(self)`, в случая с инстанцията - просто е имплицитно.
П.П. Пробвай го. :)
|
Стефан Шиваров
03.11.2024 21:07За методите Chord.is\_minor , Chord.is\_major и т.н. , очаква ли се да правим и static версия, тоест освен през инстанцията chord.is\_minor(), да могат да се викат като Chord.is\_minor(chord) ?
|
Никола Георгиев
03.11.2024 12:58```
import unittest
class TestMusicTheory(unittest.TestCase):
def test_tone_initialization(self):
c_sharp = Tone("C#")
self.assertEqual(str(c_sharp), "C#")
def test_interval_initialization(self):
minor_third = Interval(3)
self.assertEqual(str(minor_third), "minor 3rd")
def test_chord_initialization(self):
c_minor_chord = Chord(Tone("C"), Tone("D#"), Tone("G"))
self.assertEqual(str(c_minor_chord), "C-D#-G")
def test_chord_unique_tones(self):
c = Tone("C")
another_c = Tone("C")
with self.assertRaises(TypeError) as context:
Chord(c, another_c)
self.assertEqual(str(context.exception), "Cannot have a chord made of only 1 unique tone")
def test_csus4_chord(self):
c = Tone("C")
another_c = Tone("C")
f = Tone("F")
csus4_chord = Chord(c, f, another_c)
self.assertEqual(str(csus4_chord), "C-F")
def test_f_sixth_ninth_chord(self):
f_sixth_ninth_chord = Chord(Tone("F"), Tone("C"), Tone("D"), Tone("A"), Tone("G"))
self.assertEqual(str(f_sixth_ninth_chord), "F-G-A-C-D")
def test_c_minor_chord_is_minor(self):
c_minor_chord = Chord(Tone("C"), Tone("D#"), Tone("G"))
self.assertTrue(c_minor_chord.is_minor())
def test_c_not_minor_chord(self):
c_not_minor_chord = Chord(Tone("C"), Tone("D"), Tone("G"))
self.assertFalse(c_not_minor_chord.is_minor())
def test_c_major_chord_is_major(self):
c_major_chord = Chord(Tone("C"), Tone("E"), Tone("G"))
self.assertTrue(c_major_chord.is_major())
def test_c_not_major_chord(self):
c_not_major_chord = Chord(Tone("C"), Tone("D"), Tone("G"))
self.assertFalse(c_not_major_chord.is_major())
def test_c_power_chord(self):
c_power_chord = Chord(Tone("C"), Tone("F"), Tone("G"))
self.assertTrue(c_power_chord.is_power_chord())
def test_c_not_power_chord(self):
c_not_power_chord = Chord(Tone("C"), Tone("E"), Tone("G"))
self.assertFalse(c_not_power_chord.is_power_chord())
def test_add_tones(self):
c = Tone("C")
g = Tone("G")
result_chord = c + g
self.assertEqual(str(result_chord), "C-G")
def test_subtract_tones(self):
c = Tone("C")
g = Tone("G")
result_interval = g - c
self.assertEqual(str(result_interval), "perfect 5th")
def test_add_interval_to_tone(self):
c = Tone("C")
perfect_fifth = Interval(7)
result_tone = c + perfect_fifth
self.assertEqual(str(result_tone), "G")
def test_add_full_octave_to_tone(self):
c = Tone("C")
result_tone = c + Interval(12)
self.assertEqual(str(result_tone), "C")
def test_add_interval_to_tone_g(self):
g = Tone("G")
perfect_fifth = Interval(7)
result_tone = g + perfect_fifth
self.assertEqual(str(result_tone), "D")
def test_subtract_interval_from_tone(self):
c = Tone("C")
perfect_fifth = Interval(7)
result_tone = c - perfect_fifth
self.assertEqual(str(result_tone), "F")
def test_add_intervals(self):
perfect_fifth = Interval(7)
minor_third = Interval(3)
result_interval = perfect_fifth + minor_third
self.assertEqual(str(result_interval), "minor 7th")
def test_add_chord_and_tone(self):
c5_chord = Chord(Tone("C"), Tone("G"))
result_chord = c5_chord + Tone("E")
self.assertEqual(str(result_chord), "C-E-G")
def test_subtract_from_major_chord(self):
c_major_chord = Chord(Tone("C"), Tone("E"), Tone("G"))
result_chord = c_major_chord - Tone("E")
self.assertEqual(str(result_chord), "C-G")
def test_c_power_chord_subtract_g(self):
c_power_chord = Chord(Tone("C"), Tone("G"))
with self.assertRaises(TypeError) as context:
c_power_chord - Tone("G")
self.assertEqual(str(context.exception), "Cannot have a chord made of only 1 unique tone")
def test_c_power_chord_subtract_e(self):
c_power_chord = Chord(Tone("C"), Tone("G"))
with self.assertRaises(TypeError) as context:
c_power_chord - Tone("E")
self.assertEqual(str(context.exception), "Cannot remove tone E from chord C-G")
def test_add_two_chords(self):
c5_chord = Chord(Tone("C"), Tone("G"))
this_other_chord = Chord(Tone("A"), Tone("B"))
result_chord = c5_chord + this_other_chord
self.assertEqual(str(result_chord), "C-G-A-B")
def test_transpose_minor_chord(self):
c_minor_chord = Chord(Tone("C"), Tone("D#"), Tone("G"))
d_minor_chord = c_minor_chord.transposed(Interval(2))
self.assertEqual(str(d_minor_chord), "D-F-A")
def test_transpose_a_sharp_minor_chord(self):
d_minor_chord = Chord(Tone("D"), Tone("F"), Tone("A"))
a_sharp_minor_chord = d_minor_chord.transposed(-Interval(4))
self.assertEqual(str(a_sharp_minor_chord), "A#-C#-F")
if __name__ == '__main__':
unittest.main()
```
|
Георги Кунчев
03.11.2024 09:33@Дейвид_Барух, Павел зададе този въпрос и дадохме отговор. Следващият тон става главен.
|