/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.test.capedwarf.datastore.test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.google.appengine.api.NamespaceManager;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.test.capedwarf.common.test.TestBase;
import org.jboss.test.capedwarf.common.test.TestContext;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a>
* @author <a href="mailto:mluksa@redhat.com">Marko Luksa</a>
*/
public abstract class StatsQueryTestBase extends TestBase {
public static final String STAT_KIND = "__Stat_Kind__";
public static final String STAT_TOTAL = "__Stat_Total__";
public static final String STAT_NS_KIND = "__Stat_Ns_Kind__";
public static final String STAT_NS_TOTAL = "__Stat_Ns_Total__";
private DatastoreService datastore;
@Before
public void setUp() {
datastore = DatastoreServiceFactory.getDatastoreService();
}
protected static WebArchive getDefaultDeployment(boolean sync) {
TestContext context = TestContext.asDefault();
context.getProperties().put("enable.eager.datastore.stats", sync ? "sync" : "async");
return getCapedwarfDeployment(context).addClass(StatsQueryTestBase.class);
}
protected static long countBytes(Entity entity) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(entity);
out.flush();
return baos.size();
} catch (IOException e) {
throw new IllegalArgumentException("Cannot count entity: " + entity);
}
}
protected List<Entity> getStatsList(String statsKind) {
return getStatsList(statsKind, null);
}
protected List<Entity> getStatsList(String statsKind, Query.Filter filter) {
Query query = new Query(statsKind).addSort("timestamp", Query.SortDirection.DESCENDING);
if (filter != null) {
query.setFilter(filter);
}
return datastore.prepare(query).asList(FetchOptions.Builder.withDefaults());
}
protected Entity getStatsEntity(String statsKind) {
return getStatsEntity(statsKind, null);
}
protected Entity getStatsEntity(String statsKind, Query.Filter filter) {
List<Entity> list = getStatsList(statsKind, filter);
if (list.isEmpty()) {
Entity stats = new Entity(statsKind);
stats.setProperty("count", 0L);
stats.setProperty("bytes", 0L);
return stats;
} else {
return list.get(0);
}
}
protected abstract void doSync();
protected String withSuffix(String name) {
return name + "_" + getClass().getSimpleName();
}
@Test
public void testTotalStats() throws Exception {
Key k1 = null;
Key k2 = null;
try {
Entity initialStats = currentTotalStats();
NamespaceManager.set("namespace1");
Entity initialNs1Stats = currentNsTotalStats();
NamespaceManager.set("namespace2");
Entity initialNs2Stats = currentNsTotalStats();
Entity e2 = new Entity(withSuffix("SC"));
k2 = datastore.put(e2);
doSync();
assertStatsLargerBy(e2, initialNs2Stats, currentNsTotalStats());
assertStatsLargerBy(e2, initialStats, currentTotalStats());
NamespaceManager.set("namespace1");
assertStatsEqual(initialNs1Stats, currentNsTotalStats());
Entity e1 = new Entity(withSuffix("SC"));
k1 = datastore.put(e1);
doSync();
assertStatsLargerBy(e1, initialNs1Stats, currentNsTotalStats());
assertStatsLargerBy(Arrays.asList(e1, e2), initialStats, currentTotalStats());
NamespaceManager.set("namespace2");
assertStatsLargerBy(e2, initialNs2Stats, currentNsTotalStats());
datastore.delete(k2);
doSync();
assertStatsEqual(initialNs2Stats, currentNsTotalStats());
assertStatsLargerBy(e1, initialStats, currentTotalStats());
NamespaceManager.set("namespace1");
datastore.delete(k1);
doSync();
assertStatsEqual(initialNs1Stats, currentNsTotalStats());
assertStatsEqual(initialStats, currentTotalStats());
} finally {
cleanup(k1, k2);
doSync();
}
}
@Test
public void testKindStats() throws Exception {
Entity e1 = new Entity(withSuffix("SCK"));
e1.setProperty("x", "original");
Key k1 = datastore.put(e1);
doSync();
Entity e3 = new Entity(withSuffix("QWE"));
e3.setProperty("foo", "bar");
Key k3 = datastore.put(e3);
doSync();
Query.FilterPredicate filter = new Query.FilterPredicate("kind_name", Query.FilterOperator.EQUAL, withSuffix("SCK"));
Entity kindStats = getStatsEntity(STAT_KIND, filter);
long count = getCount(kindStats);
assertEquals(1L, count);
Entity e2 = new Entity(withSuffix("SCK"));
e2.setProperty("y", "replacement");
Key k2 = datastore.put(e2);
doSync();
kindStats = getStatsEntity(STAT_KIND, filter);
assertEquals(count + 1, getCount(kindStats));
List<Entity> list = getStatsList(STAT_KIND);
assertEquals(list.toString(), 1, countEntitiesMatching(list, "kind_name", withSuffix("QWE")));
datastore.delete(k2);
doSync();
kindStats = getStatsEntity(STAT_KIND, filter);
assertEquals(count, getCount(kindStats));
datastore.delete(k1, k3);
}
private void cleanup(Key... keys) {
String ns = NamespaceManager.get();
for (Key key : keys) {
if (key != null) {
NamespaceManager.set(key.getNamespace());
datastore.delete(key);
}
}
NamespaceManager.set(ns);
}
private Entity currentTotalStats() {
String ns = NamespaceManager.get();
if (ns == null || !ns.equals("")) {
NamespaceManager.set("");
}
try {
return getStatsEntity(STAT_TOTAL);
} finally {
if (ns == null || !ns.equals("")) {
NamespaceManager.set(ns);
}
}
}
private Entity currentNsTotalStats() {
return getStatsEntity(STAT_NS_TOTAL);
}
private void assertStatsEqual(Entity expected, Entity actual) {
assertEquals(getCount(expected), getCount(actual));
assertEquals(getBytes(expected), getBytes(actual));
}
private void assertStatsLargerBy(Entity entity, Entity previousStats, Entity newStats) {
assertStatsLargerBy(Collections.singleton(entity), previousStats, newStats);
}
private void assertStatsLargerBy(Collection<Entity> entities, Entity previousStats, Entity newStats) {
assertEquals("count", getCount(previousStats) + entities.size(), getCount(newStats));
assertEquals("bytes", getBytes(previousStats) + countBytes(entities), getBytes(newStats));
}
private long getBytes(Entity allStats) {
return (Long) allStats.getProperty("bytes");
}
private long getCount(Entity allStats) {
return (Long) allStats.getProperty("count");
}
private int countBytes(Collection<Entity> entities) {
int bytes = 0;
for (Entity entity : entities) {
bytes += countBytes(entity);
}
return bytes;
}
private int countEntitiesMatching(List<Entity> stats, String propertyName, String propertyValue) {
int count = 0;
for (Entity stat : stats) {
String kindName = stat.getProperty(propertyName).toString();
if (propertyValue.equals(kindName)) count++;
}
return count;
}
}