Skip to content

Commit 725debf

Browse files
criticalAYdavid-allison
authored andcommitted
test: unit test for audio recorder
1 parent 755ca3b commit 725debf

1 file changed

Lines changed: 145 additions & 0 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright (c) 2025 Ashish Yadav <mailtoashish693@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify it under
5+
* the terms of the GNU General Public License as published by the Free Software
6+
* Foundation; either version 3 of the License, or (at your option) any later
7+
* version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
10+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12+
* details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with
15+
* this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.ichi2.anki.recorder
19+
20+
import android.media.MediaRecorder
21+
import androidx.test.ext.junit.runners.AndroidJUnit4
22+
import com.ichi2.anki.RobolectricTest
23+
import com.ichi2.compat.CompatHelper
24+
import com.ichi2.testutils.EmptyApplication
25+
import io.mockk.clearAllMocks
26+
import io.mockk.every
27+
import io.mockk.mockk
28+
import io.mockk.mockkObject
29+
import io.mockk.unmockkObject
30+
import io.mockk.verify
31+
import org.junit.After
32+
import org.junit.Assert.assertEquals
33+
import org.junit.Assert.assertFalse
34+
import org.junit.Assert.assertTrue
35+
import org.junit.Test
36+
import org.junit.runner.RunWith
37+
import org.robolectric.annotation.Config
38+
import java.io.File
39+
import kotlin.test.junit5.JUnit5Asserter.assertNotNull
40+
41+
@RunWith(AndroidJUnit4::class)
42+
@Config(application = EmptyApplication::class)
43+
class AudioRecorderTest : RobolectricTest() {
44+
private val mockMediaRecorder = mockk<MediaRecorder>(relaxed = true)
45+
46+
private fun createRecorder(): AudioRecorder {
47+
mockkObject(CompatHelper)
48+
every { CompatHelper.compat.getMediaRecorder(any()) } returns mockMediaRecorder
49+
return AudioRecorder(targetContext)
50+
}
51+
52+
@After
53+
fun teardown() {
54+
unmockkObject(CompatHelper)
55+
clearAllMocks()
56+
}
57+
58+
@Test
59+
fun `start should set isRecording to true and configure recorder`() {
60+
val audioRecorder = createRecorder()
61+
audioRecorder.start()
62+
63+
assertTrue(audioRecorder.isRecording)
64+
assertNotNull("The file should still be initialized", audioRecorder.currentFile)
65+
66+
verify { mockMediaRecorder.prepare() }
67+
verify { mockMediaRecorder.start() }
68+
}
69+
70+
@Test
71+
fun `stop should set isRecording to false`() {
72+
val audioRecorder = createRecorder()
73+
audioRecorder.start()
74+
audioRecorder.stop()
75+
76+
assertFalse(audioRecorder.isRecording)
77+
verify { mockMediaRecorder.stop() }
78+
}
79+
80+
@Test
81+
fun `close should release media recorder resources`() {
82+
val audioRecorder = createRecorder()
83+
audioRecorder.start()
84+
85+
audioRecorder.close()
86+
87+
verify { mockMediaRecorder.release() }
88+
}
89+
90+
@Test
91+
fun `start should fallback to AMR if AAC configuration fails`() {
92+
every { mockMediaRecorder.prepare() } throws Exception("AAC not supported") andThen Unit
93+
94+
val audioRecorder = createRecorder()
95+
audioRecorder.start()
96+
97+
verify { mockMediaRecorder.reset() }
98+
verify(exactly = 2) { mockMediaRecorder.prepare() }
99+
100+
assertTrue(audioRecorder.isRecording)
101+
}
102+
103+
@Test
104+
fun `isRecording should remain false if mediaRecorder start fails`() {
105+
every { mockMediaRecorder.start() } throws RuntimeException("Hardware failure")
106+
107+
val audioRecorder = createRecorder()
108+
audioRecorder.start()
109+
110+
assertFalse(audioRecorder.isRecording)
111+
assertNotNull("The file should still be initialized", audioRecorder.currentFile)
112+
}
113+
114+
@Test
115+
fun `start should do nothing if already recording`() {
116+
val audioRecorder = createRecorder()
117+
audioRecorder.start()
118+
audioRecorder.start()
119+
120+
verify(exactly = 1) { mockMediaRecorder.start() }
121+
}
122+
123+
@Test
124+
fun `pause and resume should call hardware recorder`() {
125+
val audioRecorder = createRecorder()
126+
audioRecorder.start()
127+
128+
audioRecorder.pause()
129+
verify { mockMediaRecorder.pause() }
130+
131+
audioRecorder.resume()
132+
verify { mockMediaRecorder.resume() }
133+
}
134+
135+
@Test
136+
fun `start should use provided file instead of temp file`() {
137+
val customFile = File(targetContext.cacheDir, "custom_audio.3gp")
138+
139+
val audioRecorder = createRecorder()
140+
audioRecorder.start(customFile)
141+
142+
assertEquals(customFile.absolutePath, audioRecorder.currentFile?.absolutePath)
143+
verify { mockMediaRecorder.setOutputFile(customFile.absolutePath) }
144+
}
145+
}

0 commit comments

Comments
 (0)