`bundle exec rake test` (minitest, a default gem but pinned in Gemfile for reproducibility) now covers what ad-hoc scripts kept re-verifying by hand during development: Gpx/Fit/TrackStats parsing, GpxStore/LiveTrackStore file/TTL logic, LiveTrackClient's payload parsing, and app.rb's routes end to end via Rack::MockRequest. MAPTOOL_DATA_DIR (new, app.rb) lets the suite point GPX_STORE/ LIVETRACK_STORE at a throwaway directory instead of real data/ — without it every run would leave uploaded test files behind. Each lib file's old `if __FILE__ == $0` self-check is now redundant with real tests covering the same ground, so removed rather than kept as a second copy of the same assertions. Wired into ci.yml so it isn't just a local convenience.
61 lines
2.0 KiB
Ruby
61 lines
2.0 KiB
Ruby
require_relative "test_helper"
|
|
require "maptool/fit"
|
|
|
|
class FitTest < Minitest::Test
|
|
def le32(n)
|
|
[n].pack("l<")
|
|
end
|
|
|
|
# A minimal hand-built FIT file: header + one definition message (local
|
|
# type 0, global mesg 20 "record", fields position_lat/position_long,
|
|
# both sint32) + data messages. Exercises the header/definition/data
|
|
# framing without needing an external fixture file.
|
|
def build_fit(records)
|
|
definition = [0, 0].pack("CC") + [20].pack("S<") + [2].pack("C") +
|
|
[0, 4, 0x85].pack("C3") + [1, 4, 0x85].pack("C3")
|
|
definition_header = [0b01000000].pack("C")
|
|
|
|
data = records.map { |lat, lon| [0b00000000].pack("C") + le32(lat) + le32(lon) }.join
|
|
|
|
body = definition_header + definition + data
|
|
header = [12, 16, 0, 0].pack("C2S<") + le32(body.bytesize) + ".FIT"
|
|
header + body + "\x00\x00"
|
|
end
|
|
|
|
def write_fit(records)
|
|
path = File.join(Dir.mktmpdir, "test.fit")
|
|
File.binwrite(path, build_fit(records))
|
|
path
|
|
end
|
|
|
|
def test_parses_coordinates_from_record_messages
|
|
path = write_fit([[100_000_000, 200_000_000], [100_000_100, 200_000_100]])
|
|
|
|
geojson = Maptool::Fit.to_geojson(path)
|
|
coords = geojson["features"][0]["geometry"]["coordinates"][0]
|
|
|
|
assert_equal 2, coords.size
|
|
expected_lat = 100_000_000 * Maptool::Fit::SEMICIRCLE_TO_DEGREES
|
|
expected_lon = 200_000_000 * Maptool::Fit::SEMICIRCLE_TO_DEGREES
|
|
assert_in_delta expected_lat, coords[0][1], 1e-9
|
|
assert_in_delta expected_lon, coords[0][0], 1e-9
|
|
end
|
|
|
|
def test_kinds_is_track_when_positioned
|
|
assert_equal ["track"], Maptool::Fit.kinds(write_fit([[100_000_000, 200_000_000]]))
|
|
end
|
|
|
|
def test_a_file_with_no_positioned_records_has_no_features_or_kinds
|
|
path = write_fit([])
|
|
assert_empty Maptool::Fit.to_geojson(path)["features"]
|
|
assert_empty Maptool::Fit.kinds(path)
|
|
end
|
|
|
|
def test_garbage_input_raises_parse_error
|
|
path = File.join(Dir.mktmpdir, "garbage.fit")
|
|
File.binwrite(path, "not a fit file at all")
|
|
|
|
assert_raises(Maptool::Fit::ParseError) { Maptool::Fit.to_geojson(path) }
|
|
end
|
|
end
|