aboutsummaryrefslogtreecommitdiff
path: root/ride/fit_test.go
blob: 9e9e0a342b2df5868b4874a62e019ffb604adc1f (plain) (blame)
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())
}