247 lines
5.8 KiB
Go
247 lines
5.8 KiB
Go
package fitparser
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
func TestIsFIT(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "Valid FIT signature",
|
|
data: []byte{0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, '.', 'F', 'I', 'T', 0x00, 0x00},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Invalid signature",
|
|
data: []byte{0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'N', 'O', 'T', 'F', 0x00, 0x00},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Too short",
|
|
data: []byte{0x0E, 0x10},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := IsFIT(tt.data)
|
|
if result != tt.expected {
|
|
t.Errorf("IsFIT() = %v, want %v", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculateCRC(t *testing.T) {
|
|
// Test with known CRC values
|
|
data := []byte{0x0E, 0x10, 0xD9, 0x07, 0x00, 0x00, 0x00, 0x00, '.', 'F', 'I', 'T'}
|
|
crc := CalculateCRC(data)
|
|
|
|
// CRC should be non-zero for this data
|
|
if crc == 0 {
|
|
t.Errorf("CalculateCRC() returned 0, expected non-zero value")
|
|
}
|
|
|
|
// Test that same data produces same CRC
|
|
crc2 := CalculateCRC(data)
|
|
if crc != crc2 {
|
|
t.Errorf("CalculateCRC() not deterministic: got %d and %d", crc, crc2)
|
|
}
|
|
}
|
|
|
|
func TestParseHeader(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Valid 12-byte header",
|
|
data: []byte{0x0C, 0x10, 0xD9, 0x07, 0x10, 0x00, 0x00, 0x00, '.', 'F', 'I', 'T'},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "Too short",
|
|
data: []byte{0x0C, 0x10},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Invalid signature",
|
|
data: []byte{0x0C, 0x10, 0xD9, 0x07, 0x10, 0x00, 0x00, 0x00, 'N', 'O', 'P', 'E'},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
header, err := ParseHeader(tt.data)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("ParseHeader() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !tt.wantErr && header == nil {
|
|
t.Errorf("ParseHeader() returned nil header without error")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDecodeActivityFile(t *testing.T) {
|
|
// Test with a real FIT file from the SDK examples
|
|
filePath := "../example/testdata/Activity.fit"
|
|
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
t.Fatalf("Error reading test file %s: %v", filePath, err)
|
|
}
|
|
|
|
decoder, err := NewDecoder(data)
|
|
if err != nil {
|
|
t.Fatalf("NewDecoder() error = %v", err)
|
|
}
|
|
|
|
messages, err := decoder.Decode()
|
|
if err != nil {
|
|
t.Fatalf("Decode() error = %v", err)
|
|
}
|
|
|
|
if len(messages) == 0 {
|
|
t.Error("Decode() returned no messages")
|
|
}
|
|
|
|
// Print some statistics
|
|
t.Logf("Successfully decoded %d messages", len(messages))
|
|
|
|
// Count message types
|
|
msgCounts := make(map[uint16]int)
|
|
for _, msg := range messages {
|
|
msgCounts[msg.Num]++
|
|
}
|
|
|
|
t.Logf("Message type breakdown:")
|
|
for mesgNum, count := range msgCounts {
|
|
t.Logf(" %s (%d): %d messages", GetMessageName(mesgNum), mesgNum, count)
|
|
}
|
|
|
|
// Verify we have essential messages
|
|
if msgCounts[MesgNumFileId] == 0 {
|
|
t.Error("Expected at least one FileId message")
|
|
}
|
|
if msgCounts[MesgNumRecord] == 0 {
|
|
t.Error("Expected at least one Record message")
|
|
}
|
|
}
|
|
|
|
func TestDecodeSettingsFile(t *testing.T) {
|
|
filePath := "../example/testdata/Settings.fit"
|
|
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
t.Fatalf("Error reading test file %s: %v", filePath, err)
|
|
}
|
|
|
|
decoder, err := NewDecoder(data)
|
|
if err != nil {
|
|
t.Fatalf("NewDecoder() error = %v", err)
|
|
}
|
|
|
|
messages, err := decoder.Decode()
|
|
if err != nil {
|
|
t.Fatalf("Decode() error = %v", err)
|
|
}
|
|
|
|
if len(messages) == 0 {
|
|
t.Error("Decode() returned no messages")
|
|
}
|
|
|
|
t.Logf("Successfully decoded %d messages from Settings.fit", len(messages))
|
|
}
|
|
|
|
func TestCheckIntegrity(t *testing.T) {
|
|
filePath := "../example/testdata/Activity.fit"
|
|
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
t.Fatalf("Error reading test file %s: %v", filePath, err)
|
|
}
|
|
|
|
err = CheckIntegrity(data)
|
|
if err != nil {
|
|
t.Errorf("CheckIntegrity() error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestConvertSemicirclesToDegrees(t *testing.T) {
|
|
tests := []struct {
|
|
semicircles int32
|
|
expected float64
|
|
}{
|
|
{0, 0.0},
|
|
{2147483647, 180.0}, // Max positive (approximately)
|
|
{-2147483648, -180.0}, // Max negative (approximately)
|
|
{1073741824, 90.0}, // Quarter circle
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(fmt.Sprintf("%d_semicircles", tt.semicircles), func(t *testing.T) {
|
|
result := ConvertSemicirclesToDegrees(tt.semicircles)
|
|
// Allow small floating point error
|
|
if result < tt.expected-0.1 || result > tt.expected+0.1 {
|
|
t.Errorf("ConvertSemicirclesToDegrees(%d) = %f, want %f", tt.semicircles, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecode(b *testing.B) {
|
|
filePath := "../example/testdata/Activity.fit"
|
|
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
b.Fatalf("Error reading test file %s: %v", filePath, err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
decoder, err := NewDecoder(data)
|
|
if err != nil {
|
|
b.Fatalf("NewDecoder() error = %v", err)
|
|
}
|
|
|
|
_, err = decoder.Decode()
|
|
if err != nil {
|
|
b.Fatalf("Decode() error = %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecodeNoCRC(b *testing.B) {
|
|
filePath := "../example/testdata/Activity.fit"
|
|
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
b.Fatalf("Error reading test file %s: %v", filePath, err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
decoder, err := NewDecoder(data)
|
|
if err != nil {
|
|
b.Fatalf("NewDecoder() error = %v", err)
|
|
}
|
|
|
|
decoder.EnableCRCCheck(false)
|
|
_, err = decoder.Decode()
|
|
if err != nil {
|
|
b.Fatalf("Decode() error = %v", err)
|
|
}
|
|
}
|
|
}
|