/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License * at: * * http://opensource.org/licenses/ecl2.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * */ package org.opencastproject.assetmanager.impl; import static com.entwinemedia.fn.Stream.$; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import org.opencastproject.assetmanager.api.Property; import org.opencastproject.assetmanager.api.PropertyId; import org.opencastproject.assetmanager.api.PropertyName; import org.opencastproject.assetmanager.api.Value; import org.opencastproject.assetmanager.api.Value.ValueType; import org.opencastproject.assetmanager.api.Values; import org.opencastproject.assetmanager.api.fn.ARecords; import org.opencastproject.assetmanager.api.query.AResult; import org.opencastproject.assetmanager.api.query.ASelectQuery; import org.opencastproject.assetmanager.api.query.Predicate; import org.opencastproject.assetmanager.api.query.Target; import com.entwinemedia.fn.Fn; import com.entwinemedia.fn.Stream; import com.entwinemedia.fn.fns.Booleans; import org.hamcrest.Matcher; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Date; import java.util.Random; import java.util.UUID; import junitparams.JUnitParamsRunner; import junitparams.Parameters; @RunWith(JUnitParamsRunner.class) public class AbstractAssetManagerPropertyRetrievalTest extends AbstractAssetManagerTestBase { /** * Create some media packages and associate some random properties to each of them. * Then iterate all created properties and create a query for each of them. */ @Test @Parameters public void testPropertyRetrieval(final Params params) { // create a set of media packages and add them to the asset manager final String[] mps = createAndAddMediaPackagesSimple(params.mpCount, 1, 1); final Random random = new Random(System.nanoTime()); // create a set of random property names final PropertyName[] propertyNames = createRandomPropertyNames(params.propertyNameSetSize); // create a random amount of random properties for each media package final Stream<Property> props = $(mps).bind(new Fn<String, Stream<Property>>() { @Override public Stream<Property> apply(final String mp) { // create a random amount of random properties return Stream.cont(inc()).take(random.nextInt(params.maxProps - params.minProps + 1) + params.minProps).map(new Fn<Integer, Property>() { @Override public Property apply(Integer ignore) { // try to pick a free property a 100 times for (int i = 0; i < 100; i++) { // randomly select a property name final PropertyName pName = propertyNames[random.nextInt(propertyNames.length)]; // check if the selected property is already associated with the current media package final ASelectQuery doesPropertyExist = q.select(q.properties(pName)).where(q.mediaPackageId(mp)); if (sizeOf(doesPropertyExist.run().getRecords().bind(ARecords.getProperties)) == 0) { // create a property with a randomly picked value final Property p = Property.mk(PropertyId.mk(mp, pName), params.values[random.nextInt(params.values.length)]); if (am.setProperty(p)) return p; } } fail("Cannot pick another random property that has not been inserted yet"); return null; } }); } }).eval(); // evaluate stream to fill the database, otherwise unexpected results will occur due to stream laziness assertThat("Number of generated properties", sizeOf(props), allOf(greaterThanOrEqualTo(params.mpCount * params.minProps), lessThanOrEqualTo(params.mpCount * params.maxProps))); // iterate all properties and try to retrieve them from the AssetManager for (final Property prop : props) { final AResult r = q.select(params.mkTarget.apply(prop)) .where(params.mkWhere.apply(prop)) .run(); // get all properties of the result records assertThat("Number of records", r.getSize(), params.expectRecords); final Stream<Property> allProps = r.getRecords().bind(ARecords.getProperties); assertThat("Total number of properties: " + allProps.mkString(", "), sizeOf(allProps), params.expectPropertiesTotal); assertThat("Total number of snapshots", sizeOf(r.getRecords().bind(ARecords.getSnapshot)), params.expectSnapshotsTotal); final Stream<Property> findSavedProperty = r.getRecords().bind(ARecords.getProperties).filter(Booleans.eq(prop)); if (params.expectContainsSavedProperty) { assertThat("Contains saved property", findSavedProperty, hasItem(prop)); } } } private Object parametersForTestPropertyRetrieval() throws Exception { setUp(mkAbstractAssetManager()); return $a( // Fetch one property of the latest version of a media package. new Params() .propertyNameSetSize(20000) .generateProperties(1, 10) .mkTarget(new Fn<Property, Target[]>() { @Override public Target[] apply(Property p) { return $a(q.properties(p.getId().getFqn())); } }) .mkWhere(new Fn<Property, Predicate>() { @Override public Predicate apply(Property p) { return q.mediaPackageId(p.getId().getMediaPackageId()).and(q.version().isLatest()); } }) .expectRecords(equalTo(1L)) .expectPropertiesTotal(equalTo(1)) .expectSnapshotsTotal(equalTo(0)) .expectContainsSavedProperty(true), // // The difference between this fixture and the above is only an additional property predicate which // will not influence the result set. new Params() .propertyNameSetSize(10) .generateProperties(5, 5) .mkTarget(new Fn<Property, Target[]>() { @Override public Target[] apply(Property p) { return $a(q.properties(p.getId().getFqn())); } }) .mkWhere(new Fn<Property, Predicate>() { @Override public Predicate apply(Property p) { return q.mediaPackageId(p.getId().getMediaPackageId()) .and(q.version().isLatest()) .and(q.property(Value.UNTYPED, p.getId().getFqn()).exists()); } }) .expectRecords(equalTo(1L)) .expectPropertiesTotal(equalTo(1)) .expectSnapshotsTotal(equalTo(0)) .expectContainsSavedProperty(true), // // Fetch all properties of the matched records. new Params() .propertyNameSetSize(10) .generateProperties(5, 5) .mkTarget(new Fn<Property, Target[]>() { @Override public Target[] apply(Property p) { return $a(q.properties()); } }) .mkWhere(new Fn<Property, Predicate>() { @Override public Predicate apply(Property p) { final ValueType<Object> t = (ValueType<Object>) p.getValue().getType(); return q.mediaPackageId(p.getId().getMediaPackageId()) .and(q.version().isLatest()) .and(q.property(t, p.getId().getFqn()).eq(Values.getValueUntyped(p.getValue()))); } }) .expectRecords(equalTo(1L)) .expectPropertiesTotal(equalTo(5)) .expectSnapshotsTotal(equalTo(0)) .expectContainsSavedProperty(true), // new Params() .propertyNameSetSize(20) .generateProperties(5, 5) .mkTarget(new Fn<Property, Target[]>() { @Override public Target[] apply(Property p) { return $a(q.snapshot()); } }) .mkWhere(new Fn<Property, Predicate>() { @Override public Predicate apply(Property p) { final ValueType<Object> t = (ValueType<Object>) p.getValue().getType(); return q.mediaPackageId(p.getId().getMediaPackageId()) .and(q.version().isLatest()) .and(q.property(t, p.getId().getFqn()).eq(Values.getValueUntyped(p.getValue()))); } }) .expectRecords(equalTo(1L)) .expectPropertiesTotal(equalTo(0)) .expectSnapshotsTotal(equalTo(1)) .expectContainsSavedProperty(false), // // Fetch two times the same property of the latest version of a media package. new Params() .propertyNameSetSize(15) .generateProperties(1, 10) .mkTarget(new Fn<Property, Target[]>() { @Override public Target[] apply(Property p) { return $a(q.properties(p.getId().getFqn()), q.properties(p.getId().getFqn())); } }) .mkWhere(new Fn<Property, Predicate>() { @Override public Predicate apply(Property p) { return q.mediaPackageId(p.getId().getMediaPackageId()).and(q.version().isLatest()); } }) .expectRecords(equalTo(1L)) .expectPropertiesTotal(equalTo(1)) .expectSnapshotsTotal(equalTo(0)) .expectContainsSavedProperty(true), // new Params() .propertyNameSetSize(50) .generateProperties(1, 10) .mkTarget(new Fn<Property, Target[]>() { @Override public Target[] apply(Property p) { return $a(q.properties(p.getId().getFqn())); } }) .mkWhere(new Fn<Property, Predicate>() { @Override public Predicate apply(Property p) { return q.hasPropertiesOf(p.getId().getNamespace()); } }) .expectRecords(greaterThanOrEqualTo(1L)) .expectPropertiesTotal(greaterThanOrEqualTo(1)) .expectSnapshotsTotal(equalTo(0)) .expectContainsSavedProperty(true) ); } private static String randomString() { return UUID.randomUUID().toString().substring(0, 8); } private static PropertyName[] createRandomPropertyNames(int number) { final PropertyName[] ps = new PropertyName[number]; for (int i = 0; i < ps.length; i++) { ps[i] = PropertyName.mk("ns-" + randomString(), "p-" + randomString()); } return ps; } /** * Parameter holder. */ class Params { // CHECKSTYLE:OFF int mpCount = 50; Value[] values = $a(Value.mk(true), Value.mk(new Date()), Value.mk("a"), Value.mk(1L), Value.mk("b")); int propertyNameSetSize = 100; int minProps = 1; int maxProps = 10; Fn<Property, Target[]> mkTarget = new Fn<Property, Target[]>() { @Override public Target[] apply(Property property) { return new Target[0]; } }; Fn<Property, Predicate> mkWhere = new Fn<Property, Predicate>() { @Override public Predicate apply(Property property) { return q.always(); } }; Matcher<Long> expectRecords = anyOf(); Matcher<Integer> expectPropertiesTotal = anyOf(); Matcher<Integer> expectSnapshotsTotal = anyOf(); boolean expectContainsSavedProperty = true; // CHECKSTYLE:ON /** Amount of media packages to create. */ Params mpCount(int a) { this.mpCount = a; return this; } /** Set of values to select from randomly. */ Params values(Value[] a) { this.values = a; return this; } /** Number of randomly created property names to select from. */ Params propertyNameSetSize(int a) { this.propertyNameSetSize = a; return this; } /** Minimum and maximum amount of random properties per media package. */ Params generateProperties(int min, int max) { this.minProps = min; this.maxProps = max; return this; } /** Create select target. */ Params mkTarget(Fn<Property, Target[]> a) { this.mkTarget = a; return this; } /** Create where predicate. */ Params mkWhere(Fn<Property, Predicate> a) { this.mkWhere = a; return this; } /** Number of expected records. */ Params expectRecords(Matcher<Long> a) { this.expectRecords = a; return this; } /** Total number of expected properties (properties per record times number of records). */ Params expectPropertiesTotal(Matcher<Integer> a) { this.expectPropertiesTotal = a; return this; } /** Total number of expected snapshots (snapshot per record times number of records). */ Params expectSnapshotsTotal(Matcher<Integer> a) { this.expectSnapshotsTotal = a; return this; } /** Set to true if the property passed to the target and where construction functions is expected in the total set of properties. */ Params expectContainsSavedProperty(boolean a) { this.expectContainsSavedProperty = a; return this; } } }