1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.libraries.mobiledatadownload.file.common;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static org.junit.Assert.assertThrows;
20 
21 import android.net.Uri;
22 import com.google.thirdparty.robolectric.GoogleRobolectricTestRunner;
23 import org.junit.Test;
24 import org.junit.runner.RunWith;
25 
26 @RunWith(GoogleRobolectricTestRunner.class)
27 public final class FragmentTest {
28 
29   @Test
builder_empty()30   public void builder_empty() throws Exception {
31     Fragment.Builder fragment = Fragment.builder();
32     assertThat(fragment.build().toString()).isEmpty();
33   }
34 
35   @Test
parse_empty()36   public void parse_empty() throws Exception {
37     Fragment fragment = Fragment.parse("");
38     assertThat(fragment.toString()).isEmpty();
39   }
40 
41   @Test
parse_simpleParam()42   public void parse_simpleParam() throws Exception {
43     Fragment fragment = Fragment.parse("simple=true");
44     assertThat(fragment.toString()).isEqualTo("simple=true");
45     assertThat(fragment.params().get(0).key()).isEqualTo("simple");
46   }
47 
48   @Test
builder()49   public void builder() throws Exception {
50     Fragment fragment =
51         Fragment.builder()
52             .addParam(
53                 Fragment.Param.builder("paramKey")
54                     .addValue(
55                         Fragment.ParamValue.builder("paramValue")
56                             .addSubParam("subparamKey", "subparamValue")))
57             .build();
58 
59     assertThat(fragment.toString()).isEqualTo("paramKey=paramValue(subparamKey=subparamValue)");
60   }
61 
62   @Test
parse()63   public void parse() throws Exception {
64     Fragment fragment = Fragment.parse("paramKey=paramValue(subparamKey=subparamValue)");
65 
66     assertThat(fragment.toString()).isEqualTo("paramKey=paramValue(subparamKey=subparamValue)");
67 
68     Fragment.Param param = fragment.params().get(0);
69     assertThat(param.key()).isEqualTo("paramKey");
70     Fragment.ParamValue value = param.values().get(0);
71     assertThat(value.name()).isEqualTo("paramValue");
72     Fragment.SubParam subparam = value.subParams().get(0);
73     assertThat(subparam.key()).isEqualTo("subparamKey");
74     assertThat(subparam.value()).isEqualTo("subparamValue");
75   }
76 
77   @Test
parse_multipleParamsAndsubParams()78   public void parse_multipleParamsAndsubParams() throws Exception {
79     Fragment fragment = Fragment.parse("k1=v1&k2=v2(sk1=sv1)&k3=v3(sk1=sv1,sk2=sv2,sk3)");
80 
81     assertThat(fragment.toString()).isEqualTo("k1=v1&k2=v2(sk1=sv1)&k3=v3(sk1=sv1,sk2=sv2,sk3)");
82 
83     Fragment.Param p1 = fragment.params().get(0);
84     assertThat(p1.key()).isEqualTo("k1");
85     Fragment.ParamValue v1 = p1.values().get(0);
86     assertThat(v1.name()).isEqualTo("v1");
87     assertThat(v1.subParams().size()).isEqualTo(0);
88 
89     Fragment.Param p2 = fragment.params().get(1);
90     assertThat(p2.key()).isEqualTo("k2");
91     Fragment.ParamValue v2 = p2.values().get(0);
92     assertThat(v2.name()).isEqualTo("v2");
93     assertThat(v2.subParams().size()).isEqualTo(1);
94     Fragment.SubParam p2s = v2.subParams().get(0);
95     assertThat(p2s.key()).isEqualTo("sk1");
96     assertThat(p2s.value()).isEqualTo("sv1");
97 
98     Fragment.Param p3 = fragment.params().get(2);
99     assertThat(p3.key()).isEqualTo("k3");
100     Fragment.ParamValue v3 = p3.values().get(0);
101     assertThat(v3.name()).isEqualTo("v3");
102     assertThat(v3.subParams().size()).isEqualTo(3);
103     Fragment.SubParam p3s1 = v3.subParams().get(0);
104     assertThat(p3s1.key()).isEqualTo("sk1");
105     assertThat(p3s1.hasValue()).isTrue();
106     assertThat(p3s1.value()).isEqualTo("sv1");
107     Fragment.SubParam p3s2 = v3.subParams().get(1);
108     assertThat(p3s2.key()).isEqualTo("sk2");
109     assertThat(p3s2.hasValue()).isTrue();
110     assertThat(p3s2.value()).isEqualTo("sv2");
111     Fragment.SubParam p3s3 = v3.subParams().get(2);
112     assertThat(p3s3.key()).isEqualTo("sk3");
113     assertThat(p3s3.hasValue()).isFalse();
114     assertThat(p3s3.value()).isNull();
115   }
116 
117   @Test
builder_multipleValues()118   public void builder_multipleValues() throws Exception {
119     Fragment fragment =
120         Fragment.builder()
121             .addParam("unset")
122             .addParam(Fragment.Param.builder("k1").addValue("v1"))
123             .addParam(
124                 Fragment.Param.builder("k2")
125                     .addValue("v2")
126                     .addValue("v2a")
127                     .addValue(Fragment.ParamValue.builder("v2b").addSubParam("sk1", "sv1")))
128             .build();
129 
130     assertThat(fragment.toString()).isEqualTo("k1=v1&k2=v2+v2a+v2b(sk1=sv1)");
131   }
132 
133   @Test
builder_nestedMutation()134   public void builder_nestedMutation() throws Exception {
135     Fragment.Builder fragment = Fragment.parse("k0=v0a&k1=v1&k2=v2+v2a+v2b(sk1=sv1)").toBuilder();
136 
137     fragment.findParam("k0").addValue("v0b");
138     fragment.findParam("k1").findValue("v1").addSubParam("sk1", "sv1");
139     fragment.findParam("k2").findValue("v2b").addSubParam("sk2", "sv2");
140 
141     assertThat(fragment.build().toString())
142         .isEqualTo("k0=v0a+v0b&k1=v1(sk1=sv1)&k2=v2+v2a+v2b(sk1=sv1,sk2=sv2)");
143   }
144 
145   @Test
parse_multipleValues()146   public void parse_multipleValues() throws Exception {
147     Fragment fragment = Fragment.parse("k0=v0&k1=v1&k2=v2+v2a+v2b(sk1=sv1)");
148 
149     Fragment.Param p0 = fragment.params().get(0);
150     assertThat(p0.key()).isEqualTo("k0");
151     Fragment.ParamValue v0 = p0.values().get(0);
152     assertThat(v0.name()).isEqualTo("v0");
153     assertThat(v0.subParams().isEmpty()).isTrue();
154 
155     Fragment.Param p1 = fragment.params().get(1);
156     assertThat(p1.key()).isEqualTo("k1");
157     Fragment.ParamValue v1 = p1.values().get(0);
158     assertThat(v1.name()).isEqualTo("v1");
159     assertThat(v1.subParams().isEmpty()).isTrue();
160 
161     Fragment.Param p2 = fragment.params().get(2);
162     assertThat(p2.key()).isEqualTo("k2");
163     Fragment.ParamValue v2 = p2.values().get(0);
164     assertThat(v2.name()).isEqualTo("v2");
165     assertThat(v2.subParams().isEmpty()).isTrue();
166 
167     Fragment.ParamValue v2a = p2.values().get(1);
168     assertThat(v2a.name()).isEqualTo("v2a");
169     assertThat(v2a.subParams().isEmpty()).isTrue();
170 
171     Fragment.ParamValue v2b = p2.values().get(2);
172     assertThat(v2b.name()).isEqualTo("v2b");
173     assertThat(v2b.subParams().size()).isEqualTo(1);
174     Fragment.SubParam v2bs1 = v2b.subParams().get(0);
175     assertThat(v2bs1.key()).isEqualTo("sk1");
176     assertThat(v2bs1.value()).isEqualTo("sv1");
177   }
178 
179   @Test
parse_duplicateParams()180   public void parse_duplicateParams() throws Exception {
181     Fragment fragment = Fragment.parse("k=1&k=2");
182     assertThat(fragment.params().get(0).values().get(0).name()).isEqualTo("2");
183   }
184 
185   @Test
parse_duplicateParamValues()186   public void parse_duplicateParamValues() throws Exception {
187     Fragment fragment = Fragment.parse("k=1+1(x=y)");
188     assertThat(fragment.params().get(0).values().get(0).findSubParamValue("x")).isEqualTo("y");
189   }
190 
191   @Test
parse_duplicatesubParams()192   public void parse_duplicatesubParams() throws Exception {
193     Fragment fragment = Fragment.parse("k=1(x=y,x=z)");
194     assertThat(fragment.params().get(0).values().get(0).findSubParamValue("x")).isEqualTo("z");
195   }
196 
197   @Test
parse_duplicateUnsetSubParams()198   public void parse_duplicateUnsetSubParams() throws Exception {
199     Fragment fragment = Fragment.parse("k=1(x=y,x)");
200     assertThat(fragment.params().get(0).values().get(0).findSubParam("x")).isNotNull();
201     assertThat(fragment.params().get(0).values().get(0).findSubParam("x").hasValue()).isFalse();
202     assertThat(fragment.params().get(0).values().get(0).findSubParamValue("x")).isNull();
203   }
204 
205   @Test
parse_unsetSubParam()206   public void parse_unsetSubParam() throws Exception {
207     Fragment fragment = Fragment.parse("p=v(sp1)");
208     assertThat(fragment.findParam("p").findValue("v").findSubParam("sp1")).isNotNull();
209     assertThat(fragment.findParam("p").findValue("v").findSubParam("sp1").hasValue()).isFalse();
210     assertThat(fragment.findParam("p").findValue("v").findSubParamValue("sp1")).isNull();
211   }
212 
213   @Test
roundTrip()214   public void roundTrip() throws Exception {
215     Fragment fragment = Fragment.parse("a=b&c=d(e=f,g=h,i=j)+e+f");
216 
217     assertThat(fragment.toString()).isEqualTo("a=b&c=d(e=f,g=h,i=j)+e+f");
218   }
219 
220   @Test
parse_illegal()221   public void parse_illegal() throws Exception {
222     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("x"));
223     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("x="));
224 
225     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("="));
226     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("=="));
227     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("=x"));
228 
229     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("x=y("));
230     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("x=y)"));
231     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("x=y()"));
232 
233     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("x=y(=)"));
234     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("x=y(==)"));
235     assertThrows(IllegalArgumentException.class, () -> Fragment.parse("x=y(=z)"));
236   }
237 
238   @Test
parse_weirdButNotIllegal()239   public void parse_weirdButNotIllegal() throws Exception {
240     // Unencoded chars gets encoded
241     assertThat(Fragment.parse(" =y").toString()).isEqualTo("+=y");
242     assertThat(Fragment.parse(" x=y ").toString()).isEqualTo("+x=y+");
243     assertThat(Fragment.parse("()=y").toString()).isEqualTo("%28%29=y");
244     assertThat(Fragment.parse("x==y").toString()).isEqualTo("x=%3Dy");
245     assertThat(Fragment.parse("x=y(z==)").toString()).isEqualTo("x=y(z=%3D)");
246 
247     assertThat(Fragment.parse("x=y(z=)").toString()).isEqualTo("x=y(z)");
248     assertThat(Fragment.parse("+=y").toString()).isEqualTo("+=y");
249     assertThat(Fragment.parse("").toString()).isEmpty();
250     assertThat(Fragment.parse((String) null).toString()).isEmpty();
251   }
252 
253   @Test
build_escapingInvalidCharacters()254   public void build_escapingInvalidCharacters() throws Exception {
255     Fragment fragment =
256         Fragment.builder()
257             .addParam(
258                 Fragment.Param.builder("m&m")
259                     .addValue(Fragment.ParamValue.builder("2+2").addSubParam("k=", "(v)")))
260             .build();
261     assertThat(fragment.toString()).isEqualTo("m%26m=2%2B2(k%3D=%28v%29)");
262 
263     Fragment roundtrip = Fragment.parse(fragment.toString());
264     Fragment.Param mnm = roundtrip.params().get(0);
265     assertThat(mnm.key()).isEqualTo("m&m");
266     Fragment.ParamValue twoptwo = mnm.values().get(0);
267     assertThat(twoptwo.name()).isEqualTo("2+2");
268     Fragment.SubParam pqp = twoptwo.subParams().get(0);
269     assertThat(pqp.key()).isEqualTo("k=");
270     assertThat(pqp.value()).isEqualTo("(v)");
271   }
272 
273   @Test
toBuilder_shouldMakeDefensiveCopy()274   public void toBuilder_shouldMakeDefensiveCopy() throws Exception {
275     Fragment fragment = Fragment.parse("a=b(c=d)");
276     Fragment.Builder fragmentBuilder = fragment.toBuilder();
277     assertThat(fragment.toString()).isEqualTo("a=b(c=d)");
278     assertThat(fragmentBuilder.build().toString()).isEqualTo("a=b(c=d)");
279 
280     fragmentBuilder.addParam(Fragment.Param.builder("X").addValue("XX"));
281     fragmentBuilder.findParam("a").addValue("Y");
282     fragmentBuilder.findParam("a").findValue("b").addSubParam("Z", "ZZ");
283 
284     assertThat(fragment.toString()).isEqualTo("a=b(c=d)");
285     assertThat(fragmentBuilder.build().toString()).isEqualTo("a=b(c=d,Z=ZZ)+Y&X=XX");
286   }
287 
288   @Test
uri_withValidCharacters()289   public void uri_withValidCharacters() throws Exception {
290     String encodedFragmentString = "a=b+c(d=e)";
291     Uri uri = Uri.parse("a://b/c").buildUpon().encodedFragment(encodedFragmentString).build();
292     assertThat(uri.toString()).isEqualTo("a://b/c#a=b+c(d=e)");
293     assertThat(uri.getEncodedFragment()).isEqualTo(encodedFragmentString);
294     Fragment roundtrip = Fragment.parse(uri);
295     Fragment.Param a = roundtrip.params().get(0);
296     assertThat(a.key()).isEqualTo("a");
297     Fragment.ParamValue b = a.values().get(0);
298     assertThat(b.name()).isEqualTo("b");
299     Fragment.ParamValue c = a.values().get(1);
300     assertThat(c.name()).isEqualTo("c");
301     Fragment.SubParam de = c.subParams().get(0);
302     assertThat(de.key()).isEqualTo("d");
303     assertThat(de.value()).isEqualTo("e");
304   }
305 
306   @Test
uri_withInvalidCharacters()307   public void uri_withInvalidCharacters() throws Exception {
308     String encodedFragmentString = "m%26m=2%2B2(k%3D=%28v%29)";
309     Uri uri = Uri.parse("a://b/c").buildUpon().encodedFragment(encodedFragmentString).build();
310     assertThat(uri.toString()).isEqualTo("a://b/c#m%26m=2%2B2(k%3D=%28v%29)");
311     assertThat(uri.getEncodedFragment()).isEqualTo(encodedFragmentString);
312     Fragment roundtrip = Fragment.parse(uri);
313     Fragment.Param mnm = roundtrip.params().get(0);
314     assertThat(mnm.key()).isEqualTo("m&m");
315     Fragment.ParamValue twoptwo = mnm.values().get(0);
316     assertThat(twoptwo.name()).isEqualTo("2+2");
317     Fragment.SubParam pqp = twoptwo.subParams().get(0);
318     assertThat(pqp.key()).isEqualTo("k=");
319     assertThat(pqp.value()).isEqualTo("(v)");
320   }
321 }
322