• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (c) Meta Platforms, Inc. and affiliates.
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 package com.facebook.ktfmt.testutil
18 
19 import com.facebook.ktfmt.debughelpers.PrintAstVisitor
20 import com.facebook.ktfmt.format.Formatter
21 import com.facebook.ktfmt.format.FormattingOptions
22 import com.facebook.ktfmt.format.Parser
23 import com.google.common.truth.FailureMetadata
24 import com.google.common.truth.Subject
25 import com.google.common.truth.Truth
26 import org.junit.Assert
27 
28 /**
29  * Verifies the given code passes through formatting, and stays the same at the end
30  *
31  * @param code a code string that continas an optional first line made of "---" in the case
32  *   [deduceMaxWidth] is true. For example:
33  * ```
34  * --------------------
35  * // exactly 20 `-` above
36  * fun f()
37  * ```
38  *
39  * @param deduceMaxWidth if this is true the code string should start with a line of "-----" in the
40  *   beginning to indicate the max width to format by
41  */
42 fun assertFormatted(
43     code: String,
44     formattingOptions: FormattingOptions = FormattingOptions(),
45     deduceMaxWidth: Boolean = false
46 ) {
47   val first = code.lines().first()
48   var deducedCode = code
49   var maxWidth = FormattingOptions.DEFAULT_MAX_WIDTH
50   val isFirstLineAMaxWidthMarker = first.all { it == '-' }
51   if (deduceMaxWidth) {
52     if (!isFirstLineAMaxWidthMarker) {
53       throw RuntimeException(
54           "deduceMaxWidth is false, please remove the first dashes only line from the code (i.e. ---)")
55     }
56     deducedCode = code.substring(code.indexOf('\n') + 1)
57     maxWidth = first.length
58   } else {
59     if (isFirstLineAMaxWidthMarker) {
60       throw RuntimeException(
61           "When deduceMaxWidth is true the first line need to be all dashes only (i.e. ---)")
62     }
63   }
64   assertThatFormatting(deducedCode)
65       .withOptions(formattingOptions.copy(maxWidth = maxWidth))
66       .isEqualTo(deducedCode)
67 }
68 
assertThatFormattingnull69 fun assertThatFormatting(code: String): FormattedCodeSubject {
70   fun codes(): Subject.Factory<FormattedCodeSubject, String> {
71     return Subject.Factory { metadata, subject -> FormattedCodeSubject(metadata, subject) }
72   }
73   return Truth.assertAbout(codes()).that(code)
74 }
75 
76 class FormattedCodeSubject(metadata: FailureMetadata, private val code: String) :
77     Subject(metadata, code) {
78   private var options: FormattingOptions = FormattingOptions()
79   private var allowTrailingWhitespace = false
80 
withOptionsnull81   fun withOptions(options: FormattingOptions): FormattedCodeSubject {
82     this.options = options
83     return this
84   }
85 
allowTrailingWhitespacenull86   fun allowTrailingWhitespace(): FormattedCodeSubject {
87     this.allowTrailingWhitespace = true
88     return this
89   }
90 
isEqualTonull91   fun isEqualTo(expectedFormatting: String) {
92     if (!allowTrailingWhitespace && expectedFormatting.lines().any { it.endsWith(" ") }) {
93       throw RuntimeException(
94           "Expected code contains trailing whitespace, which the formatter usually doesn't output:\n" +
95               expectedFormatting
96                   .lines()
97                   .map { if (it.endsWith(" ")) "[$it]" else it }
98                   .joinToString("\n"))
99     }
100     val actualFormatting: String
101     try {
102       actualFormatting = Formatter.format(options, code)
103       if (actualFormatting != expectedFormatting) {
104         reportError(code)
105         println("# Output: ")
106         println("#".repeat(20))
107         println(actualFormatting)
108         println("# Expected: ")
109         println("#".repeat(20))
110         println(expectedFormatting)
111         println("#".repeat(20))
112         println(
113             "Need more information about the break operations?" +
114                 "Run test with assertion with \"FormattingOptions(debuggingPrintOpsAfterFormatting = true)\"")
115       }
116     } catch (e: Error) {
117       reportError(code)
118       throw e
119     }
120     Assert.assertEquals(expectedFormatting, actualFormatting)
121   }
122 
reportErrornull123   private fun reportError(code: String) {
124     val file = Parser.parse(code)
125     println("# Parse tree of input: ")
126     println("#".repeat(20))
127     file.accept(PrintAstVisitor())
128     println()
129     println("# Input: ")
130     println("#".repeat(20))
131     println(code)
132     println()
133   }
134 }
135