16
16
package com.diffplug.selfie
17
17
18
18
import com.diffplug.selfie.guts.CallStack
19
- import com.diffplug.selfie.guts.DiskSnapshotTodo
20
19
import com.diffplug.selfie.guts.DiskStorage
21
- import com.diffplug.selfie.guts.LiteralBoolean
22
- import com.diffplug.selfie.guts.LiteralFormat
23
- import com.diffplug.selfie.guts.LiteralInt
24
- import com.diffplug.selfie.guts.LiteralLong
25
- import com.diffplug.selfie.guts.LiteralString
26
- import com.diffplug.selfie.guts.LiteralValue
27
20
import com.diffplug.selfie.guts.SnapshotSystem
28
21
import com.diffplug.selfie.guts.initSnapshotSystem
29
- import com.diffplug.selfie.guts.recordCall
30
- import kotlin.io.encoding.Base64
31
- import kotlin.io.encoding.ExperimentalEncodingApi
32
- import kotlin.jvm.JvmOverloads
33
22
import kotlin.jvm.JvmStatic
34
23
35
24
/* * Static methods for creating snapshots. */
@@ -58,186 +47,14 @@ object Selfie {
58
47
}
59
48
}
60
49
61
- class DiskSelfie internal constructor(actual : Snapshot , val disk : DiskStorage ) :
62
- LiteralStringSelfie (actual) {
63
- @JvmOverloads
64
- fun toMatchDisk (sub : String = ""): DiskSelfie {
65
- val call = recordCall(false )
66
- if (system.mode.canWrite(false , call, system)) {
67
- disk.writeDisk(actual, sub, call)
68
- } else {
69
- assertEqual(disk.readDisk(sub, call), actual, system)
70
- }
71
- return this
72
- }
73
-
74
- @JvmOverloads
75
- fun toMatchDisk_TODO (sub : String = ""): DiskSelfie {
76
- val call = recordCall(false )
77
- if (system.mode.canWrite(true , call, system)) {
78
- disk.writeDisk(actual, sub, call)
79
- system.writeInline(DiskSnapshotTodo .createLiteral(), call)
80
- return this
81
- } else {
82
- throw system.fs.assertFailed(" Can't call `toMatchDisk_TODO` in ${Mode .readonly} mode!" )
83
- }
84
- }
85
- }
86
-
87
- open class LiteralStringSelfie
88
- internal constructor (
89
- protected val actual: Snapshot ,
90
- private val onlyFacets: Collection <String >? = null
91
- ) {
92
- init {
93
- if (onlyFacets != null ) {
94
- check(onlyFacets.all { it == " " || actual.facets.containsKey(it) }) {
95
- " The following facets were not found in the snapshot: ${onlyFacets.filter { actual.subjectOrFacetMaybe(it) == null }} \n available facets are: ${actual.facets.keys} "
96
- }
97
- check(onlyFacets.isNotEmpty()) {
98
- " Must have at least one facet to display, this was empty."
99
- }
100
- if (onlyFacets.contains(" " )) {
101
- check(onlyFacets.indexOf(" " ) == 0 ) {
102
- " If you're going to specify the subject facet (\"\" ), you have to list it first, this was $onlyFacets "
103
- }
104
- }
105
- }
106
- }
107
- /* * Extract a single facet from a snapshot in order to do an inline snapshot. */
108
- fun facet (facet : String ) = LiteralStringSelfie (actual, listOf (facet))
109
- /* * Extract a multiple facets from a snapshot in order to do an inline snapshot. */
110
- fun facets (vararg facets : String ) = LiteralStringSelfie (actual, facets.toList())
111
-
112
- @OptIn(ExperimentalEncodingApi ::class )
113
- private fun actualString (): String {
114
- if (actual.facets.isEmpty() || onlyFacets?.size == 1 ) {
115
- // single value doesn't have to worry about escaping at all
116
- val onlyValue = actual.subjectOrFacet(onlyFacets?.first() ? : " " )
117
- return if (onlyValue.isBinary) {
118
- Base64 .Mime .encode(onlyValue.valueBinary()).replace(" \r " , " " )
119
- } else onlyValue.valueString()
120
- } else {
121
- return serializeOnlyFacets(
122
- actual,
123
- onlyFacets
124
- ? : buildList {
125
- add(" " )
126
- addAll(actual.facets.keys)
127
- })
128
- }
129
- }
130
-
131
- @JvmOverloads
132
- fun toBe_TODO (unusedArg : Any? = null) = toBeDidntMatch(null , actualString(), LiteralString )
133
- fun toBe (expected : String ): String {
134
- val actualString = actualString()
135
- return if (actualString == expected) system.checkSrc(actualString)
136
- else toBeDidntMatch(expected, actualString, LiteralString )
137
- }
138
- }
139
-
140
50
@JvmStatic
141
51
fun <T > expectSelfie (actual : T , camera : Camera <T >) = expectSelfie(camera.snapshot(actual))
142
-
143
52
@JvmStatic fun expectSelfie (actual : String ) = expectSelfie(Snapshot .of(actual))
144
-
145
53
@JvmStatic fun expectSelfie (actual : ByteArray ) = expectSelfie(Snapshot .of(actual))
146
-
147
54
@JvmStatic fun expectSelfie (actual : Snapshot ) = DiskSelfie (actual, deferredDiskStorage)
148
-
149
- /* * Implements the inline snapshot whenever a match fails. */
150
- private fun <T : Any > toBeDidntMatch (expected : T ? , actual : T , format : LiteralFormat <T >): T {
151
- val call = recordCall(false )
152
- val writable = system.mode.canWrite(expected == null , call, system)
153
- if (writable) {
154
- system.writeInline(LiteralValue (expected, actual, format), call)
155
- return actual
156
- } else {
157
- if (expected == null ) {
158
- throw system.fs.assertFailed(" Can't call `toBe_TODO` in ${Mode .readonly} mode!" )
159
- } else {
160
- throw system.fs.assertFailed(system.mode.msgSnapshotMismatch(), expected, actual)
161
- }
162
- }
163
- }
164
-
165
- class IntSelfie (private val actual : Int ) {
166
- @JvmOverloads fun toBe_TODO (unusedArg : Any? = null) = toBeDidntMatch(null , actual, LiteralInt )
167
- fun toBe (expected : Int ) =
168
- if (actual == expected) system.checkSrc(actual)
169
- else toBeDidntMatch(expected, actual, LiteralInt )
170
- }
171
-
172
- @JvmStatic fun expectSelfie (actual : Int ) = IntSelfie (actual)
173
-
174
- class LongSelfie (private val actual : Long ) {
175
- @JvmOverloads fun toBe_TODO (unusedArg : Any? = null) = toBeDidntMatch(null , actual, LiteralLong )
176
- fun toBe (expected : Long ) =
177
- if (actual == expected) system.checkSrc(actual)
178
- else toBeDidntMatch(expected, actual, LiteralLong )
179
- }
180
-
181
55
@JvmStatic fun expectSelfie (actual : Long ) = LongSelfie (actual)
182
-
183
- class BooleanSelfie (private val actual : Boolean ) {
184
- @JvmOverloads
185
- fun toBe_TODO (unusedArg : Any? = null) = toBeDidntMatch(null , actual, LiteralBoolean )
186
- fun toBe (expected : Boolean ) =
187
- if (actual == expected) system.checkSrc(actual)
188
- else toBeDidntMatch(expected, actual, LiteralBoolean )
189
- }
190
-
56
+ @JvmStatic fun expectSelfie (actual : Int ) = IntSelfie (actual)
191
57
@JvmStatic fun expectSelfie (actual : Boolean ) = BooleanSelfie (actual)
192
- private fun assertEqual (expected : Snapshot ? , actual : Snapshot , storage : SnapshotSystem ) {
193
- when (expected) {
194
- null -> throw storage.fs.assertFailed(storage.mode.msgSnapshotNotFound())
195
- actual -> return
196
- else -> {
197
- val mismatchedKeys =
198
- sequence {
199
- yield (" " )
200
- yieldAll(expected.facets.keys)
201
- for (facet in actual.facets.keys) {
202
- if (! expected.facets.containsKey(facet)) {
203
- yield (facet)
204
- }
205
- }
206
- }
207
- .filter { expected.subjectOrFacetMaybe(it) != actual.subjectOrFacetMaybe(it) }
208
- .toList()
209
- .sorted()
210
- throw storage.fs.assertFailed(
211
- storage.mode.msgSnapshotMismatch(),
212
- serializeOnlyFacets(expected, mismatchedKeys),
213
- serializeOnlyFacets(actual, mismatchedKeys))
214
- }
215
- }
216
- }
217
-
218
- /* *
219
- * Returns a serialized form of only the given facets if they are available, silently omits
220
- * missing facets.
221
- */
222
- private fun serializeOnlyFacets (snapshot : Snapshot , keys : Collection <String >): String {
223
- val writer = StringBuilder ()
224
- for (key in keys) {
225
- if (key.isEmpty()) {
226
- SnapshotFile .writeEntry(writer, " " , null , snapshot.subjectOrFacet(key))
227
- } else {
228
- snapshot.subjectOrFacetMaybe(key)?.let { SnapshotFile .writeEntry(writer, " " , key, it) }
229
- }
230
- }
231
- val EMPTY_KEY_AND_FACET = " ╔═ ═╗\n "
232
- return if (writer.startsWith(EMPTY_KEY_AND_FACET )) {
233
- // this codepath is triggered by the `key.isEmpty()` line above
234
- writer.subSequence(EMPTY_KEY_AND_FACET .length, writer.length - 1 ).toString()
235
- } else {
236
- writer.setLength(writer.length - 1 )
237
- writer.toString()
238
- }
239
- }
240
-
241
58
@JvmStatic fun memoize (toMemoize : () -> String ) = memoize(Roundtrip .identity(), toMemoize)
242
59
243
60
@JvmStatic
@@ -257,12 +74,3 @@ object Selfie {
257
74
fun <T > memoizeBinary (roundtrip : Roundtrip <T , ByteArray >, toMemoize : () -> T ) =
258
75
MemoBinary <T >(deferredDiskStorage, roundtrip, toMemoize)
259
76
}
260
-
261
- /* *
262
- * Checks that the sourcecode of the given inline snapshot value doesn't have control comments when
263
- * in readonly mode.
264
- */
265
- private fun <T > SnapshotSystem.checkSrc (value : T ): T {
266
- mode.canWrite(false , recordCall(true ), this )
267
- return value
268
- }
0 commit comments