com.gliffy.restunit.comparator.StrictMatchComparator.java Source code

Java tutorial

Introduction

Here is the source code for com.gliffy.restunit.comparator.StrictMatchComparator.java

Source

// Copyright (c) 2008 by David Copeland
// Licensed under the Apache License, Version 2.0
// Available in LICENSE.txt with this code or
// at http://www.apache.org/licenses/LICENSE-2.0

package com.gliffy.restunit.comparator;

import java.io.*;
import java.text.*;
import java.util.*;

import com.gliffy.restunit.*;
import com.gliffy.restunit.http.*;

import org.apache.commons.logging.*;

/** A comparator that requires a strict match between received and expected results.
 * This uses the {@link StatusComparator} and {@link HeaderComparator} to check those portions of the
 * response.  The response bodies are compared via {@link #compareBodies(byte[],com.gliffy.restunit.http.ContentType,byte[],com.gliffy.restunit.http.ContentType)}.  This implementation requires
 * a byte-for-byte match.  You may subclass this and override that method to provide fuzzier matches.
 * You may also wish to override {@link #createDiffMessage(byte[],com.gliffy.restunit.http.ContentType,byte[],com.gliffy.restunit.http.ContentType)}
 * to create a meaningful diff message.
 */
public class StrictMatchComparator implements ResultComparator {
    private static final String DEFAULT_ENCODING_DESC = "system default";
    private StatusComparator itsStatusComparator = new StatusComparator();
    private HeaderComparator itsHeaderComparator = new HeaderComparator();
    private Log itsLogger = LogFactory.getLog(getClass());

    /** Compares the results as per the class Javadoc.
     * @param receivedResponse the response received.
     * @param expectedResponse the expected response, which could be a {@link com.gliffy.restunit.BodyResponse}.  If it
     * is not, the "expected body" is assumed to be null.
     * @return A matching result if the bodies, status and headers match exactly as per the test response.
     */
    public ComparisonResult compare(HttpResponse receivedResponse, RestCallResponse expectedResponse) {
        ComparisonResult result = itsStatusComparator.compare(receivedResponse, expectedResponse);
        if (result.getMatches()) {
            result = itsHeaderComparator.compare(receivedResponse, expectedResponse);
            if (result.getMatches()) {
                byte expectedBody[] = null;
                byte receivedBody[] = receivedResponse.getBody();

                ContentType expectedContentType = null;
                if (expectedResponse instanceof BodyResponse) {
                    BodyResponse bodyResponse = (BodyResponse) expectedResponse;
                    expectedBody = bodyResponse.getBody();
                    expectedContentType = ContentType.getContentType(bodyResponse.getContentType());
                }

                if (expectedContentType == null) {
                    itsLogger.debug("No content-type set in expected response, trying the headers");
                    expectedContentType = ContentType.getContentType(
                            expectedResponse.getHeaders().get(RestCallResponse.CONTENT_TYPE_HEADER));
                }
                ContentType receivedContentType = ContentType
                        .getContentType(receivedResponse.getHeaders().get(RestCallResponse.CONTENT_TYPE_HEADER));
                return compareBodies(expectedBody, expectedContentType, receivedBody, receivedContentType);
            } else {
                return result;
            }
        } else {
            return result;
        }
    }

    /** Compares the two bodies, requiring an exact match.
     * @param expectedBody the body expected, may be null
     * @param expectedContentType the content type expected by the test, or null if it wasn't specified
     * @param receivedBody the body received, may be null
     * @param receivedContentType the content type received in the response, or null if it wasn't specified
     * @return a successful match if both bodies are byte-for-byte matches.  a null body
     * and an empty body (0-length array) are considered equal.  Note that if the content type expected is a text-based
     * type the message included in the ComparisonResult will attempt to render both bits of data as strings, for purposes of
     * indicating the difference.  This will be done using the encoding's specified, or the default encoding if none is specified.
     */
    protected ComparisonResult compareBodies(byte[] expectedBody, ContentType expectedContentType,
            byte[] receivedBody, ContentType receivedContentType) {
        if (Arrays.equals(normalize(expectedBody), normalize(receivedBody))) {
            return ComparisonResult.MATCHES;
        } else {
            String diffMessage = createDiffMessage(normalize(expectedBody), expectedContentType,
                    normalize(receivedBody), receivedContentType);
            return new ComparisonResult(false, diffMessage);
        }
    }

    /** Given two differing bytestreams, and the content types attached to each, returns a string-based diff message that can help the
     * tester understand why the comparison failed.
     * @param expectedBody the body expected, will not be null (nulls from server get converted to empty arrays)
     * @param expectedContentType the content type expected by the test, or null if it wasn't specified
     * @param receivedBody the body received, will not be null (nulls from server get converted to empty arrays)
     * @param receivedContentType the content type received in the response, or null if it wasn't specified
     * @return a human-readable message explaining why the comparison failed.  This version checks to see if the expected content type is a text-based type.
     * If so, it attempts to convert the bytestreams to strings using the encodings specified in their content types.  If the expectedContentType has no encoding
     * the receivedContentType's encoding is used.  If no encodings were detected, the system default is used.  You should probably write your tests with encodings
     * because of this.
     */
    protected String createDiffMessage(byte[] expectedBody, ContentType expectedContentType, byte[] receivedBody,
            ContentType receivedContentType) {
        String extra = "";
        if ((expectedContentType != null) && (expectedContentType.getMimeType().startsWith("text/"))) {
            itsLogger.debug("Expected content type was " + expectedContentType.getMimeType());
            String expectedEncoding = expectedContentType.getEncoding();
            String receivedEncoding = receivedContentType == null ? null : receivedContentType.getEncoding();
            if (expectedEncoding == null)
                expectedEncoding = receivedEncoding;

            try {
                String expected = null;
                String received = null;
                String expectedEncodingDesc = DEFAULT_ENCODING_DESC;
                String receivedEncodingDesc = DEFAULT_ENCODING_DESC;
                if (expectedEncoding == null) {
                    expected = new String(expectedBody);
                } else {
                    expectedEncodingDesc = expectedEncoding;
                    expected = new String(expectedBody, expectedEncoding);
                }

                if (receivedEncoding == null) {
                    received = new String(receivedBody);
                } else {
                    received = new String(receivedBody, receivedEncoding);
                    receivedEncodingDesc = receivedEncoding;
                }
                return MessageFormat.format(
                        "Bodies didn't match; expected (using encoding {0}):\n'{1}'\nreceived (using encoding {2}):\n'{3}'",
                        expectedEncodingDesc, expected, receivedEncodingDesc, received);
            } catch (UnsupportedEncodingException e) {
                itsLogger.warn("Coding " + expectedEncoding + " or " + receivedEncoding + " was not supported", e);
                extra = " (while attempting to output data in string format: " + e.getMessage() + ")";
            }
        } else {
            itsLogger.debug("Expected Content-Type was "
                    + (expectedContentType == null ? "not specified" : expectedContentType.getMimeType()));
        }
        return "Bodies didn't match" + extra;
    }

    /** Normalizes the byte array to an empty array if it is null.
     * @param bytes a byte array, or null
     * @return a byte array.  if bytes was null, a size 0 array is returned
     */
    private byte[] normalize(byte[] bytes) {
        if (bytes == null)
            return new byte[0];
        else
            return bytes;
    }
}