1 /*
2  * Copyright 2024 The Android Open Source Project
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 
17 @file:Suppress("UnstableApiUsage")
18 
19 package androidx.wear.protolayout.lint
20 
21 import androidx.wear.protolayout.lint.ResponsiveLayoutDetector.Companion.EDGE_CONTENT_LAYOUT_ISSUE
22 import androidx.wear.protolayout.lint.ResponsiveLayoutDetector.Companion.PRIMARY_LAYOUT_ISSUE
23 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
24 import org.junit.Test
25 import org.junit.runner.RunWith
26 import org.junit.runners.JUnit4
27 
28 @RunWith(JUnit4::class)
29 class EdgeContentLayoutResponsiveDetectorTest : LintDetectorTest() {
getDetectornull30     override fun getDetector() = ResponsiveLayoutDetector()
31 
32     override fun getIssues() = mutableListOf(PRIMARY_LAYOUT_ISSUE, EDGE_CONTENT_LAYOUT_ISSUE)
33 
34     private val deviceParametersStub =
35         java(
36             """
37                 package androidx.wear.protolayout;
38                 public class DeviceParameters {}
39             """
40                 .trimIndent()
41         )
42 
43     private val edgeContentLayoutStub =
44         java(
45             """
46                 package androidx.wear.protolayout.material.layouts;
47 
48                 import androidx.wear.protolayout.DeviceParameters;
49 
50                 public class EdgeContentLayout {
51                     public static class Builder {
52                         public Builder(DeviceParameters deviceParameters) {}
53                         public Builder() {}
54 
55                         public Builder setResponsiveContentInsetEnabled(boolean enabled) {
56                             return this;
57                         }
58 
59                         public EdgeContentLayout build() {
60                             return new EdgeContentLayout();
61                         }
62                     }
63                 }
64             """
65                 .trimIndent()
66         )
67 
68     @Test
69     fun `edgeContentLayout with responsiveness doesn't report`() {
70         lint()
71             .files(
72                 deviceParametersStub,
73                 edgeContentLayoutStub,
74                 kotlin(
75                         """
76                         package foo
77                         import androidx.wear.protolayout.material.layouts.EdgeContentLayout
78 
79                         val layout = EdgeContentLayout.Builder(null)
80                                 .setResponsiveContentInsetEnabled(true)
81                                 .build()
82 
83                         class Bar {
84                          val layout = EdgeContentLayout.Builder(null)
85                                     .setResponsiveContentInsetEnabled(true)
86                                     .build()
87 
88                             fun build() {
89                                 val l = EdgeContentLayout.Builder(null)
90                                     .setResponsiveContentInsetEnabled(true)
91                                 return l.build()
92                             }
93 
94                             fun update() {
95                                 return EdgeContentLayout.Builder()
96                                 .setResponsiveContentInsetEnabled(true)
97                             }
98 
99                             fun build2() {
100                                 update().build()
101                             }
102 
103                             fun callRandom() {
104                               random(EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(true))
105                             }
106                             fun random(val l: EdgeContentLayout.Builder) {}
107 
108                         }
109                     """
110                     )
111                     .indented()
112             )
113             .issues(EDGE_CONTENT_LAYOUT_ISSUE)
114             .run()
115             .expectClean()
116     }
117 
118     @Test
119     fun `edgeContentLayout without responsiveness requires and fixes setter`() {
120         lint()
121             .files(
122                 deviceParametersStub,
123                 edgeContentLayoutStub,
124                 kotlin(
125                         """
126                         package foo
127                         import androidx.wear.protolayout.material.layouts.EdgeContentLayout
128 
129                         val layout = EdgeContentLayout.Builder(null)
130                                 .setResponsiveContentInsetEnabled(false)
131                                 .build()
132 
133                         class Bar {
134                          val layout = EdgeContentLayout.Builder(null)
135                                 .build()
136 
137                          val layoutFalse = EdgeContentLayout.Builder(null)
138                                     .setResponsiveContentInsetEnabled(false)
139                                 .build()
140 
141                             fun buildFalse() {
142                                 val l = EdgeContentLayout.Builder(null)
143                                     .setResponsiveContentInsetEnabled(false)
144                                 return l.build()
145                             }
146 
147                             fun update() {
148                                 val enabled = false
149                                 EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(enabled)
150                             }
151 
152                             fun build() {
153                                 update().build()
154                             }
155 
156                             fun build2() {
157                                 return EdgeContentLayout.Builder().build()
158                             }
159 
160                             fun doubleFalse() {
161                                 EdgeContentLayout.Builder()
162                                     .setResponsiveContentInsetEnabled(true)
163                                     .setResponsiveContentInsetEnabled(false)
164                             }
165 
166                             fun callRandom() {
167                               random(EdgeContentLayout.Builder())
168                             }
169                             fun random(val l: EdgeContentLayout.Builder) {}
170 
171                             fun condition(val cond: Boolean) {
172                                 val e = EdgeContentLayout.Builder()
173                                 if (cond) {
174                                   e.setResponsiveContentInsetEnabled(false)
175                                 } else {
176                                   e.setResponsiveContentInsetEnabled(true)
177                                 }
178                             }
179                         }
180                     """
181                     )
182                     .indented()
183             )
184             // To confirm they are not mixed up.
185             .issues(EDGE_CONTENT_LAYOUT_ISSUE, PRIMARY_LAYOUT_ISSUE)
186             .run()
187             .expect(
188                 """
189                     src/foo/Bar.kt:4: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
190                     setResponsiveContentInsetEnabled(true) for the best results across
191                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
192                     val layout = EdgeContentLayout.Builder(null)
193                                  ~~~~~~~~~~~~~~~~~~~~~~~~~
194                     src/foo/Bar.kt:9: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
195                     setResponsiveContentInsetEnabled(true) for the best results across
196                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
197                      val layout = EdgeContentLayout.Builder(null)
198                                   ~~~~~~~~~~~~~~~~~~~~~~~~~
199                     src/foo/Bar.kt:12: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
200                     setResponsiveContentInsetEnabled(true) for the best results across
201                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
202                      val layoutFalse = EdgeContentLayout.Builder(null)
203                                        ~~~~~~~~~~~~~~~~~~~~~~~~~
204                     src/foo/Bar.kt:17: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
205                     setResponsiveContentInsetEnabled(true) for the best results across
206                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
207                             val l = EdgeContentLayout.Builder(null)
208                                     ~~~~~~~~~~~~~~~~~~~~~~~~~
209                     src/foo/Bar.kt:24: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
210                     setResponsiveContentInsetEnabled(true) for the best results across
211                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
212                             EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(enabled)
213                             ~~~~~~~~~~~~~~~~~~~~~~~~~
214                     src/foo/Bar.kt:32: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
215                     setResponsiveContentInsetEnabled(true) for the best results across
216                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
217                             return EdgeContentLayout.Builder().build()
218                                    ~~~~~~~~~~~~~~~~~~~~~~~~~
219                     src/foo/Bar.kt:36: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
220                     setResponsiveContentInsetEnabled(true) for the best results across
221                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
222                             EdgeContentLayout.Builder()
223                             ~~~~~~~~~~~~~~~~~~~~~~~~~
224                     src/foo/Bar.kt:42: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
225                     setResponsiveContentInsetEnabled(true) for the best results across
226                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
227                           random(EdgeContentLayout.Builder())
228                                  ~~~~~~~~~~~~~~~~~~~~~~~~~
229                     src/foo/Bar.kt:47: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
230                     setResponsiveContentInsetEnabled(true) for the best results across
231                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
232                             val e = EdgeContentLayout.Builder()
233                                     ~~~~~~~~~~~~~~~~~~~~~~~~~
234                     0 errors, 9 warnings
235                 """
236                     .trimIndent()
237             )
238             .expectFixDiffs(
239                 """
240                     Fix for src/foo/Bar.kt line 4: Call setResponsiveContentInsetEnabled(true) on layouts:
241                     @@ -5 +5
242                     -         .setResponsiveContentInsetEnabled(false)
243                     +         .setResponsiveContentInsetEnabled(true)
244                     Fix for src/foo/Bar.kt line 9: Call setResponsiveContentInsetEnabled(true) on layouts:
245                     @@ -9 +9
246                     -  val layout = EdgeContentLayout.Builder(null)
247                     +  val layout = EdgeContentLayout.Builder(null).setResponsiveContentInsetEnabled(true)
248                     Fix for src/foo/Bar.kt line 12: Call setResponsiveContentInsetEnabled(true) on layouts:
249                     @@ -13 +13
250                     -             .setResponsiveContentInsetEnabled(false)
251                     +             .setResponsiveContentInsetEnabled(true)
252                     Fix for src/foo/Bar.kt line 17: Call setResponsiveContentInsetEnabled(true) on layouts:
253                     @@ -18 +18
254                     -             .setResponsiveContentInsetEnabled(false)
255                     +             .setResponsiveContentInsetEnabled(true)
256                     Fix for src/foo/Bar.kt line 24: Call setResponsiveContentInsetEnabled(true) on layouts:
257                     @@ -24 +24
258                     -         EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(enabled)
259                     +         EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(true)
260                     Fix for src/foo/Bar.kt line 32: Call setResponsiveContentInsetEnabled(true) on layouts:
261                     @@ -32 +32
262                     -         return EdgeContentLayout.Builder().build()
263                     +         return EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(true).build()
264                     Fix for src/foo/Bar.kt line 36: Call setResponsiveContentInsetEnabled(true) on layouts:
265                     @@ -38 +38
266                     -             .setResponsiveContentInsetEnabled(false)
267                     +             .setResponsiveContentInsetEnabled(true)
268                     Fix for src/foo/Bar.kt line 42: Call setResponsiveContentInsetEnabled(true) on layouts:
269                     @@ -42 +42
270                     -       random(EdgeContentLayout.Builder())
271                     +       random(EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(true))
272                     Fix for src/foo/Bar.kt line 47: Call setResponsiveContentInsetEnabled(true) on layouts:
273                     @@ -49 +49
274                     -           e.setResponsiveContentInsetEnabled(false)
275                     +           e.setResponsiveContentInsetEnabled(true)
276                 """
277                     .trimIndent()
278             )
279     }
280 
281     @Test
282     fun `edgeContentLayout with responsiveness doesn't report (Java)`() {
283         lint()
284             .files(
285                 deviceParametersStub,
286                 edgeContentLayoutStub,
287                 java(
288                         """
289                         package foo;
290                         import androidx.wear.protolayout.material.layouts.EdgeContentLayout;
291 
292                         class Bar {
293                             EdgeContentLayout layout = new EdgeContentLayout.Builder(null)
294                                     .setResponsiveContentInsetEnabled(true)
295                                     .build();
296 
297                             EdgeContentLayout build() {
298                                 EdgeContentLayout l = new EdgeContentLayout.Builder(null)
299                                     .setResponsiveContentInsetEnabled(true);
300                                 return l.build();
301                             }
302 
303                             EdgeContentLayout update() {
304                                 return new EdgeContentLayout.Builder()
305                                     .setResponsiveContentInsetEnabled(true);
306                             }
307 
308                             EdgeContentLayout build2() {
309                                 update().build();
310                             }
311                         }
312                     """
313                     )
314                     .indented()
315             )
316             .issues(EDGE_CONTENT_LAYOUT_ISSUE)
317             .run()
318             .expectClean()
319     }
320 
321     @Test
edgeContentLayout without responsiveness requires and fixes setter (Java)null322     fun `edgeContentLayout without responsiveness requires and fixes setter (Java)`() {
323         lint()
324             .files(
325                 deviceParametersStub,
326                 edgeContentLayoutStub,
327                 java(
328                         """
329                         package foo;
330                         import androidx.wear.protolayout.material.layouts.EdgeContentLayout;
331 
332                         class Bar {
333                             EdgeContentLayout layout = new EdgeContentLayout.Builder(null)
334                                 .build();
335 
336                             EdgeContentLayout layoutFalse = new EdgeContentLayout.Builder(null)
337                                     .setResponsiveContentInsetEnabled(false)
338                                 .build();
339 
340                             EdgeContentLayout buildFalse() {
341                                 EdgeContentLayout l = new EdgeContentLayout.Builder(null)
342                                     .setResponsiveContentInsetEnabled(false);
343                                 return l.build();
344                             }
345 
346                             void update() {
347                                 boolean enabled = false;
348                                 new EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(enabled);
349                             }
350 
351                             void build() {
352                                 update().build();
353                             }
354 
355                             EdgeContentLayout build2() {
356                                 return new EdgeContentLayout.Builder().build();
357                             }
358 
359                             void doubleFalse() {
360                                 new EdgeContentLayout.Builder()
361                                     .setResponsiveContentInsetEnabled(true)
362                                     .setResponsiveContentInsetEnabled(false);
363                             }
364                         }
365                     """
366                     )
367                     .indented()
368             )
369             .issues(EDGE_CONTENT_LAYOUT_ISSUE)
370             .run()
371             .expect(
372                 """
373                     src/foo/Bar.java:5: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
374                     setResponsiveContentInsetEnabled(true) for the best results across
375                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
376                         EdgeContentLayout layout = new EdgeContentLayout.Builder(null)
377                                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
378                     src/foo/Bar.java:8: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
379                     setResponsiveContentInsetEnabled(true) for the best results across
380                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
381                         EdgeContentLayout layoutFalse = new EdgeContentLayout.Builder(null)
382                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
383                     src/foo/Bar.java:13: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
384                     setResponsiveContentInsetEnabled(true) for the best results across
385                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
386                             EdgeContentLayout l = new EdgeContentLayout.Builder(null)
387                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
388                     src/foo/Bar.java:20: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
389                     setResponsiveContentInsetEnabled(true) for the best results across
390                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
391                             new EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(enabled);
392                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
393                     src/foo/Bar.java:28: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
394                     setResponsiveContentInsetEnabled(true) for the best results across
395                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
396                             return new EdgeContentLayout.Builder().build();
397                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
398                     src/foo/Bar.java:32: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
399                     setResponsiveContentInsetEnabled(true) for the best results across
400                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
401                             new EdgeContentLayout.Builder()
402                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
403                     0 errors, 6 warnings
404                 """
405                     .trimIndent()
406             )
407             .expectFixDiffs(
408                 """
409                     Fix for src/foo/Bar.java line 5: Call setResponsiveContentInsetEnabled(true) on layouts:
410                     @@ -5 +5
411                     -     EdgeContentLayout layout = new EdgeContentLayout.Builder(null)
412                     +     EdgeContentLayout layout = new EdgeContentLayout.Builder(null).setResponsiveContentInsetEnabled(true)
413                     Fix for src/foo/Bar.java line 8: Call setResponsiveContentInsetEnabled(true) on layouts:
414                     @@ -9 +9
415                     -             .setResponsiveContentInsetEnabled(false)
416                     +             .setResponsiveContentInsetEnabled(true)
417                     Fix for src/foo/Bar.java line 13: Call setResponsiveContentInsetEnabled(true) on layouts:
418                     @@ -14 +14
419                     -             .setResponsiveContentInsetEnabled(false);
420                     +             .setResponsiveContentInsetEnabled(true);
421                     Fix for src/foo/Bar.java line 20: Call setResponsiveContentInsetEnabled(true) on layouts:
422                     @@ -20 +20
423                     -         new EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(enabled);
424                     +         new EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(true);
425                     Fix for src/foo/Bar.java line 28: Call setResponsiveContentInsetEnabled(true) on layouts:
426                     @@ -28 +28
427                     -         return new EdgeContentLayout.Builder().build();
428                     +         return new EdgeContentLayout.Builder().setResponsiveContentInsetEnabled(true).build();
429                     Fix for src/foo/Bar.java line 32: Call setResponsiveContentInsetEnabled(true) on layouts:
430                     @@ -34 +34
431                     -             .setResponsiveContentInsetEnabled(false);
432                     +             .setResponsiveContentInsetEnabled(true);
433                 """
434                     .trimIndent()
435             )
436     }
437 
438     @Test
edgeContentLayout false reportnull439     fun `edgeContentLayout false report`() {
440         lint()
441             .files(
442                 deviceParametersStub,
443                 edgeContentLayoutStub,
444                 kotlin(
445                         """
446                         package foo
447                         import androidx.wear.protolayout.material.layouts.EdgeContentLayout
448 
449 
450                         fun doubleTrue() {
451                             EdgeContentLayout.Builder()
452                                 .setResponsiveContentInsetEnabled(false)
453                                 .setResponsiveContentInsetEnabled(true)
454                         }
455                     """
456                     )
457                     .indented()
458             )
459             .issues(EDGE_CONTENT_LAYOUT_ISSUE)
460             .run()
461             .expect(
462                 """
463                     src/foo/test.kt:6: Warning: EdgeContentLayout used, but responsiveness isn't set: Please call
464                     setResponsiveContentInsetEnabled(true) for the best results across
465                     different screen sizes and update to the looks of layout. [ProtoLayoutEdgeContentLayoutResponsive]
466                         EdgeContentLayout.Builder()
467                         ~~~~~~~~~~~~~~~~~~~~~~~~~
468                     0 errors, 1 warnings
469                 """
470                     .trimIndent()
471             )
472             .expectFixDiffs(
473                 """
474                     Fix for src/foo/test.kt line 6: Call setResponsiveContentInsetEnabled(true) on layouts:
475                     @@ -7 +7
476                     -         .setResponsiveContentInsetEnabled(false)
477                     @@ -9 +8
478                     +         .setResponsiveContentInsetEnabled(true)
479                 """
480                     .trimIndent()
481             )
482     }
483 }
484