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