The solution I came up with is to create an AutoPlugin for sbt that does what you need.
Create a new file in project folder named DingPlugin.scala with following content
import sbt.Keys._
import sbt.PluginTrigger.AllRequirements
import sbt._
object DingPlugin extends AutoPlugin {
private def wavPath = "/path/to/wav"
private def playSound(wav: String) = ???
override def trigger = AllRequirements
override def projectSettings: Seq[Def.Setting[_]] = Seq(
Test / test := (Test / test).andFinally(playSound(wavPath)).value
)
}
The main point is the method andFinally that
defines a new task that runs the original task and evaluates a side effect regardless of whether the original task succeeded. The result of the task is the result of the original task
The implementation of playSound is derived from this question with the following code
private def playSound(wav: String): Unit = {
import java.io.BufferedInputStream
import java.io.FileInputStream
import javax.sound.sampled.AudioSystem
val clip = AudioSystem.getClip
val inputStream =
AudioSystem.getAudioInputStream(
new BufferedInputStream(new FileInputStream(wav))
)
clip.open(inputStream)
clip.start()
Thread.sleep(clip.getMicrosecondLength / 1000)
}
Now, you can just run sbt test and it will play a sound regardless of whether test succeeded or not.