/*
* Copyright 2015 MongoDB, Inc.
*
* Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.mongodb.connection;
import com.mongodb.event.CommandEvent;
import com.mongodb.event.CommandFailedEvent;
import com.mongodb.event.CommandListener;
import com.mongodb.event.CommandStartedEvent;
import com.mongodb.event.CommandSucceededEvent;
import org.bson.BsonDocument;
import org.bson.BsonDocumentWriter;
import org.bson.BsonDouble;
import org.bson.BsonInt32;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.BsonValueCodecProvider;
import org.bson.codecs.Codec;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class TestCommandListener implements CommandListener {
private final List<CommandEvent> events = new ArrayList<CommandEvent>();
private static final CodecRegistry CODEC_REGISTRY_HACK;
static {
CODEC_REGISTRY_HACK = CodecRegistries.fromProviders(new BsonValueCodecProvider(),
new CodecProvider() {
@Override
@SuppressWarnings("unchecked")
public <T> Codec<T> get(final Class<T> clazz, final CodecRegistry registry) {
// Use BsonDocumentCodec even for a private sub-class of BsonDocument
if (BsonDocument.class.isAssignableFrom(clazz)) {
return (Codec<T>) new BsonDocumentCodec(registry);
}
return null;
}
});
}
public void reset() {
events.clear();
}
public List<CommandEvent> getEvents() {
return events;
}
@Override
public void commandStarted(final CommandStartedEvent event) {
events.add(new CommandStartedEvent(event.getRequestId(), event.getConnectionDescription(), event.getDatabaseName(),
event.getCommandName(),
event.getCommand() == null ? null : getWritableClone(event.getCommand())));
}
private BsonDocument getWritableClone(final BsonDocument original) {
BsonDocument clone = new BsonDocument();
BsonDocumentWriter writer = new BsonDocumentWriter(clone);
new BsonDocumentCodec(CODEC_REGISTRY_HACK).encode(writer, original, EncoderContext.builder().build());
return clone;
}
@Override
public void commandSucceeded(final CommandSucceededEvent event) {
events.add(new CommandSucceededEvent(event.getRequestId(), event.getConnectionDescription(), event.getCommandName(),
event.getResponse() == null ? null : event.getResponse().clone(),
event.getElapsedTime(TimeUnit.NANOSECONDS)));
}
@Override
public void commandFailed(final CommandFailedEvent event) {
events.add(event);
}
public void eventsWereDelivered(final List<CommandEvent> expectedEvents) {
assertEquals(expectedEvents.size(), events.size());
int currentlyExpectedRequestId = 0;
for (int i = 0; i < events.size(); i++) {
CommandEvent actual = events.get(i);
CommandEvent expected = expectedEvents.get(i);
if (actual instanceof CommandStartedEvent) {
currentlyExpectedRequestId = actual.getRequestId();
} else {
assertEquals(currentlyExpectedRequestId, actual.getRequestId());
}
assertEventEquivalence(actual, expected);
}
}
public void eventWasDelivered(final CommandEvent expectedEvent, final int index) {
assertTrue(events.size() > index);
assertEventEquivalence(events.get(index), expectedEvent);
}
private void assertEventEquivalence(final CommandEvent actual, final CommandEvent expected) {
assertEquals(expected.getClass(), actual.getClass());
assertEquals(expected.getConnectionDescription(), actual.getConnectionDescription());
assertEquals(expected.getCommandName(), actual.getCommandName());
if (actual.getClass().equals(CommandStartedEvent.class)) {
assertEquivalence((CommandStartedEvent) actual, (CommandStartedEvent) expected);
} else if (actual.getClass().equals(CommandSucceededEvent.class)) {
assertEquivalence((CommandSucceededEvent) actual, (CommandSucceededEvent) expected);
} else if (actual.getClass().equals(CommandFailedEvent.class)) {
assertEquivalence((CommandFailedEvent) actual, (CommandFailedEvent) expected);
} else {
throw new UnsupportedOperationException("Unsupported event type: " + actual.getClass());
}
}
private void assertEquivalence(final CommandFailedEvent actual, final CommandFailedEvent expected) {
assertEquals(expected.getThrowable(), actual.getThrowable());
}
private void assertEquivalence(final CommandSucceededEvent actual, final CommandSucceededEvent expected) {
if (actual.getResponse() == null) {
assertNull(expected.getResponse());
} else {
// ignore extra elements in the actual response
assertTrue("Expected response contains elements not in the actual response",
massageResponse(actual.getResponse()).entrySet()
.containsAll(massageResponse(expected.getResponse()).entrySet()));
}
}
private BsonDocument massageResponse(final BsonDocument response) {
BsonDocument massagedResponse = getWritableClone(response);
// massage numbers to the same BSON type
if (massagedResponse.containsKey("ok")) {
massagedResponse.put("ok", new BsonDouble(response.getNumber("ok").doubleValue()));
}
if (massagedResponse.containsKey("n")) {
massagedResponse.put("n", new BsonInt32(response.getNumber("n").intValue()));
}
return massagedResponse;
}
private void assertEquivalence(final CommandStartedEvent actual, final CommandStartedEvent expected) {
assertEquals(expected.getDatabaseName(), actual.getDatabaseName());
assertEquals(expected.getCommand(), actual.getCommand());
}
}