246
fitparser/decoder_test.go
Normal file
246
fitparser/decoder_test.go
Normal file
@@ -0,0 +1,246 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user