Measurement from a source providing multiple Measurements.
* @throws UnsupportedFileVersion If the binary file is from a deprecated or not yet supported file format version.
*/
- Measurement read() throws IOException, InvalidLifecycleEvents, NoSuchMeasurement, UnsupportedFileVersion;
+ Measurement read() throws IOException, InvalidLifecycleEvents, NoSuchMeasurement, UnsupportedFileVersion, NoTracksRecorded;
/**
* @return A list with all the valid {@link MeasurementIdentifier} within the deserializable data.
* @throws IOException If reading data fails
diff --git a/libs/deserializer/src/main/java/de/cyface/deserializer/UnzippedPhoneDataDeserializer.java b/libs/deserializer/src/main/java/de/cyface/deserializer/UnzippedPhoneDataDeserializer.java
index 534c6e1..070520b 100644
--- a/libs/deserializer/src/main/java/de/cyface/deserializer/UnzippedPhoneDataDeserializer.java
+++ b/libs/deserializer/src/main/java/de/cyface/deserializer/UnzippedPhoneDataDeserializer.java
@@ -35,6 +35,7 @@
import java.util.List;
import java.util.UUID;
+import de.cyface.model.NoTracksRecorded;
import org.apache.commons.lang3.Validate;
import de.cyface.deserializer.exceptions.InvalidLifecycleEvents;
@@ -152,7 +153,7 @@ public class UnzippedPhoneDataDeserializer extends PhoneDataDeserializer {
}
@Override
- public Measurement read() throws InvalidLifecycleEvents, NoSuchMeasurement {
+ public Measurement read() throws InvalidLifecycleEvents, NoSuchMeasurement, NoTracksRecorded {
try (final var connection = createConnection()) {
PreparedStatement measurementExistsStatement = connection.prepareStatement(MEASUREMENT_QUERY);
measurementExistsStatement.setLong(1, measurementNumber);
diff --git a/libs/deserializer/src/main/java/de/cyface/deserializer/ZippedPhoneDataDeserializer.java b/libs/deserializer/src/main/java/de/cyface/deserializer/ZippedPhoneDataDeserializer.java
index 8fc8be6..4b3ff2b 100644
--- a/libs/deserializer/src/main/java/de/cyface/deserializer/ZippedPhoneDataDeserializer.java
+++ b/libs/deserializer/src/main/java/de/cyface/deserializer/ZippedPhoneDataDeserializer.java
@@ -30,6 +30,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
+import de.cyface.model.NoTracksRecorded;
import org.apache.commons.lang3.Validate;
import de.cyface.deserializer.exceptions.InvalidLifecycleEvents;
@@ -127,7 +128,7 @@ public class ZippedPhoneDataDeserializer extends PhoneDataDeserializer {
}
@Override
- public Measurement read() throws IOException, InvalidLifecycleEvents, NoSuchMeasurement {
+ public Measurement read() throws IOException, InvalidLifecycleEvents, NoSuchMeasurement, NoTracksRecorded {
if (!isUnzipped) {
this.databaseFile = unzipAndReturnMatching(sqliteDatabasePath, "/measures");
this.accelerationPaths = unzip(accelerationsFilePath);
diff --git a/libs/deserializer/src/test/java/de/cyface/deserializer/BinaryFormatDeserializerTest.java b/libs/deserializer/src/test/java/de/cyface/deserializer/BinaryFormatDeserializerTest.java
index 6c9d8dc..3b7fe44 100644
--- a/libs/deserializer/src/test/java/de/cyface/deserializer/BinaryFormatDeserializerTest.java
+++ b/libs/deserializer/src/test/java/de/cyface/deserializer/BinaryFormatDeserializerTest.java
@@ -50,6 +50,7 @@
import java.util.List;
import java.util.UUID;
+import de.cyface.model.NoTracksRecorded;
import org.hamcrest.Matcher;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -109,7 +110,7 @@ class BinaryFormatDeserializerTest {
*/
@Test
@DisplayName("Happy Path")
- void test() throws IOException, InvalidLifecycleEvents, UnsupportedFileVersion {
+ void test() throws IOException, InvalidLifecycleEvents, UnsupportedFileVersion, NoTracksRecorded {
// Arrange
final var identifier = new MeasurementIdentifier("test", 1);
try (final var testData = testData(identifier)) {
@@ -223,7 +224,7 @@ void test() throws IOException, InvalidLifecycleEvents, UnsupportedFileVersion {
*/
@DisplayName("Happy Path test for the serialization and deserialization of 3d points.")
@Test
- void testSerializeDeserialize() throws IOException, InvalidLifecycleEvents {
+ void testSerializeDeserialize() throws IOException, InvalidLifecycleEvents, NoTracksRecorded {
// Arrange - Events: start, stop (1 track)
final var batches = 100;
diff --git a/libs/deserializer/src/test/java/de/cyface/deserializer/TrackBuilderTest.java b/libs/deserializer/src/test/java/de/cyface/deserializer/TrackBuilderTest.java
index 53ee5c5..ea6639c 100644
--- a/libs/deserializer/src/test/java/de/cyface/deserializer/TrackBuilderTest.java
+++ b/libs/deserializer/src/test/java/de/cyface/deserializer/TrackBuilderTest.java
@@ -18,23 +18,32 @@
*/
package de.cyface.deserializer;
+import static de.cyface.model.Event.EventType.LIFECYCLE_PAUSE;
+import static de.cyface.model.Event.EventType.LIFECYCLE_RESUME;
+import static de.cyface.model.Event.EventType.LIFECYCLE_START;
+import static de.cyface.model.Event.EventType.LIFECYCLE_STOP;
+import static de.cyface.model.Event.EventType.MODALITY_TYPE_CHANGE;
+import static de.cyface.model.MetaData.SUPPORTED_VERSIONS;
import static de.cyface.model.Modality.BICYCLE;
import static de.cyface.model.Modality.WALKING;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
-import java.util.NoSuchElementException;
import java.util.UUID;
import de.cyface.deserializer.exceptions.InvalidLifecycleEvents;
+import de.cyface.model.Measurement;
+import de.cyface.model.MetaData;
+import de.cyface.model.NoTracksRecorded;
import de.cyface.model.Point3DImpl;
+import de.cyface.serializer.GeoLocation;
import org.apache.commons.lang3.Validate;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import de.cyface.model.Event;
@@ -81,12 +90,12 @@ void testBuild() throws InvalidLifecycleEvents {
final var locationRecords = generateLocationRecords(numberOfLocations,
new Long[] {1000L, 1500L, 3500L, 4000L}, identifier);
final var events = new ArrayList+ * Instead of fixing the location iterator in the middle of a campaign, we just make this error fail softly + * and make it fail as INFO when we have very few locations: + * - e.g. no resume: <= (1+0+1)*2 locations => INFO + * - e.g. 1 resume: <= (1+1+1)**2 locations => INFO + * As soon as one track has > 3 locations this error should not occur. + *
+ * (1+1+1) because: with 1 resume and 2*2+1 locations it's possible, that + * - track 1: 2 location, in-between: 1 locations, track 2: 2 locations, after stop: 1 location + *
+ * If there are much more locations than resume events, this must be logged as WARN at least. (or crash)
+ * (and show the number of locations and resume events involved)
+ */
+ @Test
+ @DisplayName("TrackBuilder creates one track for short final lifecycle segment")
+ void testTrackBuilderWithShortFinalSegment() throws InvalidLifecycleEvents, NoTracksRecorded {
+ // Given
+ final var identifier = new MeasurementIdentifier("test", 1);
+ final var events = List.of(
+ new Event(LIFECYCLE_START, 1746429259640L, ""),
+ new Event(MODALITY_TYPE_CHANGE, 1746429259640L, "BICYCLE"),
+ new Event(LIFECYCLE_PAUSE, 1746429328310L, ""),
+ new Event(LIFECYCLE_RESUME, 1746445158656L, ""),
+ new Event(LIFECYCLE_PAUSE, 1746445161947L, ""),
+ new Event(LIFECYCLE_RESUME, 1746518861627L, ""),
+ new Event(LIFECYCLE_STOP, 1746518872724L, "")
+ );
+
+ final var locations = List.of(
+ // This narrow case with only 2 locations produces the crash as we always loose the first location
+ new GeoLocation(48.123509, 11.372924, 1746518871000L, 1.35f, 29.5f),
+ new GeoLocation(48.123484, 11.372941, 1746518872000L, 1.77f, 27.0f),
+ // But with only 3 more location this does not happen, as 2 locations form a track
+ new GeoLocation(48.123485, 11.372942, 1746518872001L, 1.77f, 27.0f)
+ );
+
+ // No sensor data for this test
+ final var accelerations = List.