View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.http.impl.cache;
28  
29  import java.time.Instant;
30  import java.time.temporal.ChronoUnit;
31  import java.util.Arrays;
32  import java.util.Iterator;
33  import java.util.Objects;
34  import java.util.Set;
35  
36  import org.apache.hc.client5.http.cache.HttpCacheEntry;
37  import org.apache.hc.client5.http.cache.Resource;
38  import org.apache.hc.client5.http.cache.ResourceIOException;
39  import org.apache.hc.core5.http.Header;
40  import org.hamcrest.BaseMatcher;
41  import org.hamcrest.Description;
42  import org.hamcrest.Matcher;
43  
44  public class HttpCacheEntryMatcher extends BaseMatcher<HttpCacheEntry> {
45  
46      private final HttpCacheEntry expectedValue;
47  
48      public HttpCacheEntryMatcher(final HttpCacheEntry expectedValue) {
49          this.expectedValue = expectedValue;
50      }
51  
52      @Override
53      public boolean matches(final Object item) {
54          if (item instanceof HttpCacheEntry) {
55              try {
56                  final HttpCacheEntry otherValue = (HttpCacheEntry) item;
57  
58                  if (!setEqual(expectedValue.getVariants(), otherValue.getVariants())) {
59                      return false;
60                  }
61                  if (!Objects.equals(expectedValue.getRequestMethod(), otherValue.getRequestMethod())) {
62                      return false;
63                  }
64                  if (!Objects.equals(expectedValue.getRequestURI(), otherValue.getRequestURI())) {
65                      return false;
66                  }
67                  if (!headersEqual(expectedValue.requestHeaderIterator(), otherValue.requestHeaderIterator())) {
68                      return false;
69                  }
70                  if (!instantEqual(expectedValue.getRequestInstant(), otherValue.getRequestInstant())) {
71                      return false;
72                  }
73                  if (expectedValue.getStatus() != otherValue.getStatus()) {
74                      return false;
75                  }
76                  if (!headersEqual(expectedValue.headerIterator(), otherValue.headerIterator())) {
77                      return false;
78                  }
79                  if (!instantEqual(expectedValue.getResponseInstant(), otherValue.getResponseInstant())) {
80                      return false;
81                  }
82                  final Resource expectedResource = expectedValue.getResource();
83                  final byte[] expectedContent = expectedResource != null ? expectedResource.get() : null;
84                  final Resource otherResource = otherValue.getResource();
85                  final byte[] otherContent = otherResource != null ? otherResource.get() : null;
86                  return Arrays.equals(expectedContent, otherContent);
87              } catch (final ResourceIOException ex) {
88                  throw new RuntimeException(ex);
89              }
90          }
91          return false;
92      }
93  
94      private static boolean instantEqual(final Instant expected, final Instant other) {
95          final Instant expectedMs = expected != null ? expected.truncatedTo(ChronoUnit.MILLIS) : null;
96          final Instant otherMs = other != null ? other.truncatedTo(ChronoUnit.MILLIS) : null;
97          return Objects.equals(expectedMs, otherMs);
98      }
99  
100     private static boolean headersEqual(final Iterator<Header> expected, final Iterator<Header> other) {
101         while (expected.hasNext()) {
102             final Header h1 = expected.next();
103             if (!other.hasNext()) {
104                 return false;
105             }
106             final Header h2 = other.next();
107             if (!h1.getName().equals(h2.getName()) || !Objects.equals(h1.getValue(), h2.getValue())) {
108                 return false;
109             }
110         }
111         if (other.hasNext()) {
112             return false;
113         }
114         return true;
115     }
116 
117     private static boolean setEqual(final Set<?> expected, final Set<?> actual) {
118         if (expected.size() != actual.size()) {
119             return false;
120         }
121         return actual.containsAll(expected);
122     }
123 
124     @Override
125     public void describeTo(final Description description) {
126         description.appendValue(expectedValue);
127     }
128 
129     public static Matcher<HttpCacheEntry> equivalent(final HttpCacheEntry target) {
130         return new HttpCacheEntryMatcher(target);
131     }
132 
133 }