package audio import ( "bytes" "context" "io" "os" "testing" "time" ) func TestPlayWav(t *testing.T) { // 跳过测试如果没有测试文件 testFile := "testdata/test.wav" if _, err := os.Stat(testFile); os.IsNotExist(err) { t.Skip("测试文件不存在:", testFile) } f, err := os.Open(testFile) if err != nil { t.Fatalf("打开测试文件失败: %v", err) } defer f.Close() ctx := context.Background() err = PlayWav(ctx, f) if err != nil { t.Fatalf("PlayWav 失败: %v", err) } } func TestPlayMP3(t *testing.T) { testFile := "testdata/test.mp3" if _, err := os.Stat(testFile); os.IsNotExist(err) { t.Skip("测试文件不存在:", testFile) } f, err := os.Open(testFile) if err != nil { t.Fatalf("打开测试文件失败: %v", err) } defer f.Close() ctx := context.Background() err = PlayMP3(ctx, f) if err != nil { t.Fatalf("PlayMP3 失败: %v", err) } } func TestPlayContextCancellation(t *testing.T) { testFile := "testdata/test.mp3" if _, err := os.Stat(testFile); os.IsNotExist(err) { t.Skip("测试文件不存在:", testFile) } f, err := os.Open(testFile) if err != nil { t.Fatalf("打开测试文件失败: %v", err) } defer f.Close() // 创建一个会被快速取消的 context ctx, cancel := context.WithCancel(context.Background()) // 启动播放后立即取消 done := make(chan error, 1) go func() { done <- PlayMP3(ctx, f) }() time.Sleep(10 * time.Millisecond) cancel() err = <-done if err != context.Canceled { t.Errorf("期望 context.Canceled 错误,得到: %v", err) } } // TestMonoToStereoReader 测试单声道转立体声 func TestMonoToStereoReader(t *testing.T) { // 创建测试数据:4个单声道样本(8字节) monoData := []byte{ 0x00, 0x10, // 样本1: 0x1000 = 4096 0x00, 0x20, // 样本2: 0x2000 = 8192 0x00, 0x30, // 样本3: 0x3000 = 12288 0x00, 0x40, // 样本4: 0x4000 = 16384 } reader := &monoToStereoReader{src: bytes.NewReader(monoData)} output := make([]byte, 16) // 应该产生8个样本(16字节) n, err := reader.Read(output) if err != nil { t.Fatalf("读取失败: %v", err) } if n != 16 { t.Fatalf("期望读取16字节,实际读取%d字节", n) } // 验证立体声输出(每个单声道样本被复制到左右声道) expected := []byte{ 0x00, 0x10, 0x00, 0x10, // 样本1: 左=0x1000, 右=0x1000 0x00, 0x20, 0x00, 0x20, // 样本2: 左=0x2000, 右=0x2000 0x00, 0x30, 0x00, 0x30, // 样本3: 左=0x3000, 右=0x3000 0x00, 0x40, 0x00, 0x40, // 样本4: 左=0x4000, 右=0x4000 } if !bytes.Equal(output, expected) { t.Errorf("立体声转换不正确\n期望: %x\n实际: %x", expected, output) } } // TestMonoToStereoReaderStreaming 测试流式读取 func TestMonoToStereoReaderStreaming(t *testing.T) { // 创建较大的测试数据 monoData := make([]byte, 1000) for i := range monoData { monoData[i] = byte(i % 256) } reader := &monoToStereoReader{src: bytes.NewReader(monoData)} totalRead := 0 buf := make([]byte, 32) // 小缓冲区 for { n, err := reader.Read(buf) totalRead += n if err == io.EOF { break } if err != nil { t.Fatalf("流式读取失败: %v", err) } if n == 0 { t.Fatal("读取返回0字节但未EOF") } } // 1000字节单声道应该转换为2000字节立体声 expectedTotal := 2000 if totalRead != expectedTotal { t.Fatalf("期望总共读取%d字节,实际读取%d字节", expectedTotal, totalRead) } } // TestMonoToStereoReaderPartialRead 测试部分读取 func TestMonoToStereoReaderPartialRead(t *testing.T) { monoData := []byte{0x00, 0x10, 0x00, 0x20, 0x00, 0x30} // 3个单声道样本 reader := &monoToStereoReader{src: bytes.NewReader(monoData)} // 第一次读取:请求6字节输出(只能读取1个单声道样本=4字节输出) buf1 := make([]byte, 6) n1, err := reader.Read(buf1) if err != nil { t.Fatalf("第一次读取失败: %v", err) } if n1 != 4 { t.Fatalf("第一次读取期望4字节,实际%d字节", n1) } // 第二次读取:请求10字节输出(读取剩余2个单声道样本=8字节输出) buf2 := make([]byte, 10) n2, err := reader.Read(buf2) if err != nil { t.Fatalf("第二次读取失败: %v", err) } // 剩余2个单声道样本转换为8字节立体声 if n2 != 8 { t.Fatalf("第二次读取期望8字节,实际%d字节", n2) } // 第三次读取:应该返回EOF buf3 := make([]byte, 10) n3, err := reader.Read(buf3) if err != io.EOF { t.Fatalf("第三次读取期望EOF,实际: %v", err) } if n3 != 0 { t.Fatalf("第三次读取EOF时期望0字节,实际%d字节", n3) } }