1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
package ride_test
import (
"bytes"
"encoding/binary"
"testing"
"time"
"github.com/martinlehoux/biking_home/ride"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tormoder/fit"
)
func encodeFIT(t *testing.T, fileType fit.FileType, records []*fit.RecordMsg) []byte {
t.Helper()
file, err := fit.NewFile(fileType, fit.NewHeader(fit.V20, true))
require.NoError(t, err)
file.FileId.Manufacturer = fit.ManufacturerGarmin
file.FileId.Type = fileType
file.FileId.TimeCreated = time.Date(2026, 8, 1, 7, 30, 0, 0, time.UTC)
switch fileType {
case fit.FileTypeCourse:
course, err := file.Course()
require.NoError(t, err)
course.Records = records
case fit.FileTypeActivity:
activity, err := file.Activity()
require.NoError(t, err)
activity.Records = records
}
var buf bytes.Buffer
require.NoError(t, fit.Encode(&buf, file, binary.LittleEndian))
return buf.Bytes()
}
func fitRecord(lat, lon, elevationM float64, ts time.Time) *fit.RecordMsg {
rec := fit.NewRecordMsg()
rec.Timestamp = ts
rec.PositionLat = fit.NewLatitudeDegrees(lat)
rec.PositionLong = fit.NewLongitudeDegrees(lon)
rec.Altitude = uint16((elevationM + 500) * 5)
rec.HeartRate = 0xFF
rec.Cadence = 0xFF
rec.Power = 0xFFFF
return rec
}
func TestFITRideParserParsesCourseFile(t *testing.T) {
base := time.Date(2026, 8, 1, 7, 30, 0, 0, time.UTC)
data := encodeFIT(t, fit.FileTypeCourse, []*fit.RecordMsg{
fitRecord(43.0, 5.0, 100, base),
fitRecord(43.0, 5.0, 101, base.Add(5*time.Second)), // stationary: skipped
fitRecord(43.001, 5.001, 200, base.Add(10*time.Second)),
})
parsed, err := (ride.FITRideParser{}).Parse(bytes.NewReader(data))
require.NoError(t, err)
require.Equal(t, 2, parsed.Len())
assert.Equal(t, 0.0, parsed.Distances[0])
assert.Greater(t, parsed.Distances[1], 0.0)
assert.Equal(t, []float64{100, 200}, parsed.Elevations)
assert.Equal(t, []time.Time{base, base.Add(10 * time.Second)}, parsed.Timestamps())
_, hasHR := parsed.HeartRates()
assert.False(t, hasHR, "course records carry no telemetry")
}
func TestFITRideParserRejectsFileWithoutTrack(t *testing.T) {
data := encodeFIT(t, fit.FileTypeSettings, nil)
_, err := (ride.FITRideParser{}).Parse(bytes.NewReader(data))
require.Error(t, err)
assert.Equal(t, "fit file type is Settings, not Activity or Course", err.Error())
}
|