Skip to content

Commit

Permalink
openhab#16308 Added test for larger ImageItems and the limit of 16 MB
Browse files Browse the repository at this point in the history
Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
  • Loading branch information
ulbi committed Feb 3, 2024
1 parent 950bc8b commit 2fcac9c
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,12 @@ public void store(Item item, @Nullable String alias) {
obj.put(MongoDBFields.FIELD_REALNAME, realItemName);
obj.put(MongoDBFields.FIELD_TIMESTAMP, new Date());
obj.put(MongoDBFields.FIELD_VALUE, value);
collection.insertOne(obj);

try {
collection.insertOne(obj);
} catch (org.bson.BsonMaximumSizeExceededException e) {
logger.error("Document size exceeds maximum size of 16MB. Item {} not persisted.", name);
throw e;
}
logger.debug("MongoDB save {}={}", name, value);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,15 @@
*/
package org.openhab.persistence.mongodb.internal;

import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.LinkedHashMap;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;

import javax.measure.Unit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.bson.Document;
import org.bson.types.Binary;
Expand All @@ -34,6 +32,8 @@
import org.openhab.core.persistence.FilterCriteria.Operator;
import org.openhab.core.types.*;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This class handles the conversion of types between openHAB and MongoDB.
Expand All @@ -44,7 +44,7 @@
*/
public class MongoDBTypeConversions {

private static final Logger logger = LoggerFactory.getLogger(MongoDBPersistenceService.class);
private static final Logger logger = LoggerFactory.getLogger(MongoDBTypeConversions.class);
/**
* A map of converters that convert openHAB states to MongoDB compatible types.
* Each converter is a function that takes an openHAB state and returns an object that can be stored in MongoDB.
Expand Down Expand Up @@ -89,48 +89,47 @@ public static Object convertValue(State state) {
return new QuantityType<>(value.toString());
}
if (unit != null) {
return new QuantityType<>(((Number)value).doubleValue(), unit);
return new QuantityType<>(((Number) value).doubleValue(), unit);
} else {
return new DecimalType(((Number)value).doubleValue());
return new DecimalType(((Number) value).doubleValue());
}
});
ITEM_STATE_CONVERTERS.put(ColorItem.class, (item, doc) -> {
Object value = doc.get(MongoDBFields.FIELD_VALUE);
if (value instanceof String) {
return new HSBType(value.toString());
}
else {
logger.warn("HSBType ({}) value is not a valid string: {}", doc.getString(MongoDBFields.FIELD_REALNAME), value);
return new HSBType("0,0,0");
}
Object value = doc.get(MongoDBFields.FIELD_VALUE);
if (value instanceof String) {
return new HSBType(value.toString());
} else {
logger.warn("HSBType ({}) value is not a valid string: {}", doc.getString(MongoDBFields.FIELD_REALNAME),
value);
return new HSBType("0,0,0");
}
});
ITEM_STATE_CONVERTERS.put(DimmerItem.class, (item, doc) -> {
Object value = doc.get(MongoDBFields.FIELD_VALUE);
if (value instanceof Integer) {
return new PercentType((Integer)value);
} else {
return new PercentType(((Number)value).intValue());
}
Object value = doc.get(MongoDBFields.FIELD_VALUE);
if (value instanceof Integer) {
return new PercentType((Integer) value);
} else {
return new PercentType(((Number) value).intValue());
}
});
ITEM_STATE_CONVERTERS.put(SwitchItem.class,
(item, doc) -> OnOffType.valueOf(doc.getString(MongoDBFields.FIELD_VALUE)));
ITEM_STATE_CONVERTERS.put(ContactItem.class,
(item, doc) -> OpenClosedType.valueOf(doc.getString(MongoDBFields.FIELD_VALUE)));
ITEM_STATE_CONVERTERS.put(RollershutterItem.class, (item, doc) -> {
Object value = doc.get(MongoDBFields.FIELD_VALUE);
if (value instanceof Integer) {
return new PercentType((Integer)value);
} else {
return new PercentType(((Number)value).intValue());
}
Object value = doc.get(MongoDBFields.FIELD_VALUE);
if (value instanceof Integer) {
return new PercentType((Integer) value);
} else {
return new PercentType(((Number) value).intValue());
}
});
ITEM_STATE_CONVERTERS.put(DateTimeItem.class, (item, doc) -> {
Object value = doc.get(MongoDBFields.FIELD_VALUE);
if (value instanceof String) {
return new DateTimeType(ZonedDateTime.parse(doc.getString(MongoDBFields.FIELD_VALUE)));
}
else {
return new DateTimeType(ZonedDateTime.ofInstant(((Date)value).toInstant(), ZoneId.systemDefault()));
} else {
return new DateTimeType(ZonedDateTime.ofInstant(((Date) value).toInstant(), ZoneId.systemDefault()));
}
});
ITEM_STATE_CONVERTERS.put(LocationItem.class,
Expand All @@ -146,9 +145,9 @@ public static Object convertValue(State state) {
String type = fieldValue.getString(MongoDBFields.FIELD_VALUE_TYPE);
Binary data = fieldValue.get(MongoDBFields.FIELD_VALUE_DATA, Binary.class);
return new RawType(data.getData(), type);
}
else {
logger.warn("ImageItem ({}) value is not a Document: {}", doc.getString(MongoDBFields.FIELD_REALNAME), value);
} else {
logger.warn("ImageItem ({}) value is not a Document: {}", doc.getString(MongoDBFields.FIELD_REALNAME),
value);
return new RawType(new byte[0], "application/octet-stream");
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import static org.mockito.Mockito.*;

import java.time.LocalDate;

import java.time.ZonedDateTime;
import java.util.Date;
import java.util.HashMap;
Expand All @@ -40,10 +39,8 @@

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import org.bson.types.ObjectId;
import com.mongodb.client.MongoDatabase;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
Expand Down Expand Up @@ -101,6 +98,30 @@ public static <T extends GenericItem, S extends State> T createItem(Class<T> ite
}
}

private static RawType createFakeImage(int size) {
byte[] data = new byte[size];
for (int i = 0; i < size; i++) {
data[i] = (byte) (i % 256);
}
return new RawType(data, "image/png");
}

/**
* Provides a stream of arguments for parameterized tests. To test various image sizes
*
* @return A stream of arguments for parameterized tests.
*/
public static Stream<Arguments> provideOpenhabImageItemsInDifferentSizes() {
return Stream.of(
Arguments.of(DataCreationHelper.createItem(ImageItem.class, "ImageItem1kB", createFakeImage(1024))),
Arguments.of(
DataCreationHelper.createItem(ImageItem.class, "ImageItem1MB", createFakeImage(1024 * 1024))),
Arguments.of(DataCreationHelper.createItem(ImageItem.class, "ImageItem10MB",
createFakeImage(10 * 1024 * 1024))),
Arguments.of(DataCreationHelper.createItem(ImageItem.class, "ImageItem20MB",
createFakeImage(20 * 1024 * 1024))));
}

/**
* Provides a stream of arguments for parameterized tests. Each argument is an instance of a specific
* GenericItem subclass with a set state.
Expand Down Expand Up @@ -272,33 +293,33 @@ public static ListAppender<ILoggingEvent> setupLogger(Class<?> loggerClass, Leve
return listAppender;
}

private static Object convertValue(State state) {
Object value;
if (state instanceof PercentType) {
PercentType type = (PercentType) state;
value = type.toBigDecimal().doubleValue();
} else if (state instanceof DateTimeType) {
DateTimeType type = (DateTimeType) state;
value = Date.from(type.getZonedDateTime().toInstant());
} else if (state instanceof DecimalType) {
DecimalType type = (DecimalType) state;
value = type.toBigDecimal().doubleValue();
} else {
value = state.toString();
}
return value;
private static Object convertValue(State state) {
Object value;
if (state instanceof PercentType) {
PercentType type = (PercentType) state;
value = type.toBigDecimal().doubleValue();
} else if (state instanceof DateTimeType) {
DateTimeType type = (DateTimeType) state;
value = Date.from(type.getZonedDateTime().toInstant());
} else if (state instanceof DecimalType) {
DecimalType type = (DecimalType) state;
value = type.toBigDecimal().doubleValue();
} else {
value = state.toString();
}
return value;
}

public static void storeOldData(MongoCollection<Document> collection, String realItemName, State state) {
// use the old way to store data
Object value = convertValue(state);
public static void storeOldData(MongoCollection<Document> collection, String realItemName, State state) {
// use the old way to store data
Object value = convertValue(state);

Document obj = new Document();
obj.put(MongoDBFields.FIELD_ID, new ObjectId());
obj.put(MongoDBFields.FIELD_ITEM, realItemName);
obj.put(MongoDBFields.FIELD_REALNAME, realItemName);
obj.put(MongoDBFields.FIELD_TIMESTAMP, new Date());
obj.put(MongoDBFields.FIELD_VALUE, value);
collection.insertOne(obj);
}
Document obj = new Document();
obj.put(MongoDBFields.FIELD_ID, new ObjectId());
obj.put(MongoDBFields.FIELD_ITEM, realItemName);
obj.put(MongoDBFields.FIELD_REALNAME, realItemName);
obj.put(MongoDBFields.FIELD_TIMESTAMP, new Date());
obj.put(MongoDBFields.FIELD_VALUE, value);
collection.insertOne(obj);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@

import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -606,14 +604,14 @@ public void testStoreAllOpenhabItemTypesSingleCollection(GenericItem item) {
}

/**
* Tests the store and query method of the MongoDBPersistenceService with all types of openHAB items.
* Tests the store and query method for various image sizes of the MongoDBPersistenceService
* Each item is queried with the type from one collection in the MongoDB database.
*
* @param item The item to store in the database.
*/
@ParameterizedTest
@MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideOpenhabItemTypes")
public void testQueryAllOpenhabItemTypesSingleCollection(GenericItem item) {
@MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideOpenhabImageItemsInDifferentSizes")
public void testStoreAndQueryyLargerImages(ImageItem item) {
// Preparation
DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
try {
Expand All @@ -626,7 +624,16 @@ public void testQueryAllOpenhabItemTypesSingleCollection(GenericItem item) {
Mockito.when(setupResult.itemRegistry.getItem(item.getName())).thenReturn(item);
} catch (ItemNotFoundException e) {
}
service.store(item, null);
try {
service.store(item, null);
} catch (org.bson.BsonMaximumSizeExceededException e) {
if (item.getName().equals("ImageItem20MB")) {
// this is expected
return;
} else {
throw e;
}
}

// Execution
FilterCriteria filter = DataCreationHelper.createFilterCriteria(item.getName());
Expand All @@ -640,7 +647,8 @@ public void testQueryAllOpenhabItemTypesSingleCollection(GenericItem item) {
}

/**
* Tests the old way of storing data and query method of the MongoDBPersistenceService with all types of openHAB items.
* Tests the old way of storing data and query method of the MongoDBPersistenceService with all types of openHAB
* items.
* Each item is queried with the type from one collection in the MongoDB database.
*
* @param item The item to store in the database.
Expand All @@ -667,7 +675,7 @@ public void testOldDataQueryAllOpenhabItemTypesSingleCollection(GenericItem item
item.setState(new RawType(new byte[0], "application/octet-stream"));
} else if (item instanceof ColorItem) {
item.setState(new HSBType("0,0,0"));
}
}

// Execution
FilterCriteria filter = DataCreationHelper.createFilterCriteria(item.getName());
Expand All @@ -676,10 +684,9 @@ public void testOldDataQueryAllOpenhabItemTypesSingleCollection(GenericItem item

if (item instanceof DateTimeItem) {
// verify just the date part
assertEquals(((DateTimeType)item.getState()).getZonedDateTime().toLocalDate(),
((DateTimeType)result.iterator().next().getState()).getZonedDateTime().toLocalDate());
}
else {
assertEquals(((DateTimeType) item.getState()).getZonedDateTime().toLocalDate(),
((DateTimeType) result.iterator().next().getState()).getZonedDateTime().toLocalDate());
} else {
VerificationHelper.verifyQueryResult(result, item.getState());
}
} finally {
Expand Down

0 comments on commit 2fcac9c

Please sign in to comment.