|
16 | 16 |
|
17 | 17 | package com.google.cloud.storage;
|
18 | 18 |
|
19 |
| -import static org.junit.Assert.assertEquals; |
| 19 | +import static com.google.common.truth.Truth.assertThat; |
20 | 20 | import static org.junit.Assert.assertNotNull;
|
21 | 21 |
|
22 | 22 | import com.google.api.core.ApiClock;
|
23 | 23 | import com.google.auth.oauth2.ServiceAccountCredentials;
|
| 24 | +import com.google.cloud.Tuple; |
24 | 25 | import com.google.cloud.conformance.storage.v1.SigningV4Test;
|
25 | 26 | import com.google.cloud.conformance.storage.v1.TestFile;
|
26 | 27 | import com.google.cloud.conformance.storage.v1.UrlStyle;
|
|
32 | 33 | import java.io.IOException;
|
33 | 34 | import java.io.InputStream;
|
34 | 35 | import java.io.InputStreamReader;
|
| 36 | +import java.net.URI; |
35 | 37 | import java.util.ArrayList;
|
| 38 | +import java.util.Arrays; |
36 | 39 | import java.util.Collection;
|
37 | 40 | import java.util.List;
|
| 41 | +import java.util.Map; |
| 42 | +import java.util.Objects; |
38 | 43 | import java.util.concurrent.TimeUnit;
|
39 | 44 | import java.util.concurrent.atomic.AtomicLong;
|
| 45 | +import java.util.stream.Collectors; |
40 | 46 | import org.junit.Rule;
|
41 | 47 | import org.junit.Test;
|
42 | 48 | import org.junit.rules.TestName;
|
@@ -128,7 +134,9 @@ public void test() {
|
128 | 134 | SignUrlOption.withQueryParams(testData.getQueryParametersMap()),
|
129 | 135 | style)
|
130 | 136 | .toString();
|
131 |
| - assertEquals(testData.getExpectedUrl(), signedUrl); |
| 137 | + SmarterUrl expected = SmarterUrl.of(URI.create(testData.getExpectedUrl())); |
| 138 | + SmarterUrl actual = SmarterUrl.of(URI.create(signedUrl)); |
| 139 | + assertThat(actual).isEqualTo(expected); |
132 | 140 | }
|
133 | 141 |
|
134 | 142 | /**
|
@@ -166,4 +174,56 @@ public static Collection<Object[]> testCases() throws IOException {
|
166 | 174 | }
|
167 | 175 | return data;
|
168 | 176 | }
|
| 177 | + |
| 178 | + /** |
| 179 | + * Equals on {@link URI} or {@link java.net.URL} perform string comparison on the full query |
| 180 | + * string. However, query strings are not order dependent. This class essentially provides a |
| 181 | + * smarter equals and hashcode for a url taking into account a query string is not order |
| 182 | + * dependent. |
| 183 | + */ |
| 184 | + private static final class SmarterUrl { |
| 185 | + private final String path; |
| 186 | + private final Map<String, String> queryStringParameters; |
| 187 | + |
| 188 | + private SmarterUrl(String path, Map<String, String> queryStringParameters) { |
| 189 | + this.path = path; |
| 190 | + this.queryStringParameters = queryStringParameters; |
| 191 | + } |
| 192 | + |
| 193 | + @Override |
| 194 | + public boolean equals(Object o) { |
| 195 | + if (this == o) { |
| 196 | + return true; |
| 197 | + } |
| 198 | + if (!(o instanceof SmarterUrl)) { |
| 199 | + return false; |
| 200 | + } |
| 201 | + SmarterUrl that = (SmarterUrl) o; |
| 202 | + return Objects.equals(path, that.path) |
| 203 | + && Objects.equals(queryStringParameters, that.queryStringParameters); |
| 204 | + } |
| 205 | + |
| 206 | + @Override |
| 207 | + public int hashCode() { |
| 208 | + return Objects.hash(path, queryStringParameters); |
| 209 | + } |
| 210 | + |
| 211 | + private static SmarterUrl of(URI uri) { |
| 212 | + String path = uri.getRawPath(); |
| 213 | + String rawQuery = uri.getRawQuery(); |
| 214 | + String[] split = rawQuery.split("&"); |
| 215 | + Map<String, String> queryStringParameters = |
| 216 | + Arrays.stream(split) |
| 217 | + .map( |
| 218 | + qp -> { |
| 219 | + // use indexOf instead of split, just in case an equals is part of the value |
| 220 | + int i = qp.indexOf('='); |
| 221 | + String k = qp.substring(0, i); |
| 222 | + String v = qp.substring(i + 1); |
| 223 | + return Tuple.of(k, v); |
| 224 | + }) |
| 225 | + .collect(Collectors.toMap(Tuple::x, Tuple::y)); |
| 226 | + return new SmarterUrl(path, queryStringParameters); |
| 227 | + } |
| 228 | + } |
169 | 229 | }
|
0 commit comments