wangzhibo
2026-08-16 ae6f40460dcd56af6c5f60ba52c883854c3bac55
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package nodomain.freeyourgadget.gadgetbridge.export;
 
import com.google.gson.internal.bind.util.ISO8601Utils;
 
import org.junit.Test;
import org.xml.sax.SAXException;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
 
import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
 
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.export.ActivityTrackExporter.GPXTrackEmptyException;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
import nodomain.freeyourgadget.gadgetbridge.test.TestBase;
 
public class GPXExporterTest extends TestBase {
    @Test
    public void shouldCreateValidGpxFromSimulatedData() throws IOException, ParseException, GPXTrackEmptyException, SAXException {
        final List<ActivityPoint> points = readActivityPoints("/GPXExporterTest-SampleTracks.csv");
 
        final GPXExporter gpxExporter = new GPXExporter();
        gpxExporter.setCreator("Gadgetbridge Test");
        final ActivityTrack track = createTestTrack(points);
 
        final File tempFile = File.createTempFile("gpx-exporter-test-track", ".gpx");
        tempFile.deleteOnExit();
 
        gpxExporter.performExport(track, tempFile);
        validateGpxFile(tempFile);
    }
 
    @Test
    public void shouldCreateValidGpxFromSimulatedDataWithHeartrate() throws IOException, ParseException, GPXTrackEmptyException, ParserConfigurationException, SAXException {
        final List<ActivityPoint> points = readActivityPoints("/GPXExporterTest-SampleTracksHR.csv");
 
        final GPXExporter gpxExporter = new GPXExporter();
        gpxExporter.setCreator("Gadgetbridge Test");
        final ActivityTrack track = createTestTrack(points);
 
        final File tempFile = File.createTempFile("gpx-exporter-test-track", ".gpx");
        tempFile.deleteOnExit();
 
        gpxExporter.performExport(track, tempFile);
        validateGpxFile(tempFile);
    }
 
    private ActivityTrack createTestTrack(List<ActivityPoint> points) {
        final User user = new User();
        user.setName("Test User");
 
        Device device = new Device();
        device.setName("Test Device");
 
        final ActivityTrack track = new ActivityTrack();
        track.setName("Test Track");
        track.setBaseTime(new Date());
        track.setUser(user);
        track.setDevice(device);
 
        for (final ActivityPoint point : points) {
            track.addTrackPoint(point);
        }
        return track;
    }
 
    private List<ActivityPoint> readActivityPoints(String resourcePath) throws IOException, ParseException {
        final List<ActivityPoint> points = new ArrayList<>();
        try (final InputStream inputStream = getClass().getResourceAsStream(resourcePath)) {
            try (final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
                String nextLine = reader.readLine();
                while (nextLine != null) {
                    final String[] pieces = nextLine.split("\\s+");
                    final ActivityPoint point = new ActivityPoint();
                    point.setLocation(new GPSCoordinate(
                            Double.parseDouble(pieces[0]),
                            Double.parseDouble(pieces[1]),
                            Double.parseDouble(pieces[2]))
                    );
 
                    final int dateIndex;
                    if (pieces.length == 5) {
                        point.setHeartRate(Integer.parseInt(pieces[3]));
                        dateIndex = 4;
                    } else {
                        dateIndex = 3;
                    }
                    // Not sure about this parser but seemed safe to use
                    point.setTime(ISO8601Utils.parse(pieces[dateIndex], new ParsePosition(0)));
 
                    points.add(point);
                    nextLine = reader.readLine();
                }
            }
        }
        return points;
    }
 
    private void validateGpxFile(File tempFile) throws SAXException, IOException {
        final Source xmlFile = new StreamSource(tempFile);
        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        final Schema schema = schemaFactory.newSchema(new StreamSource(getClass().getResourceAsStream("/gpx.xsd")));
        final Validator validator = schema.newValidator();
        validator.validate(xmlFile);
    }
}