• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

skuzzle / snapshot-tests / 1

12 Feb 2025 03:38PM UTC coverage: 86.61% (+0.03%) from 86.577%
1

Pull #109

jenkins

skuzzle
Remove old file after merge
Pull Request #109: 2.0 dev

1947 of 2248 relevant lines covered (86.61%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.31
/../snapshot-tests-core/src/main/java/de/skuzzle/test/snapshots/impl/SnapshotTestContext.java
1
package de.skuzzle.test.snapshots.impl;
2

3
import java.lang.reflect.Method;
4
import java.nio.file.Path;
5
import java.util.Collection;
6
import java.util.stream.Collectors;
7
import java.util.stream.Stream;
8

9
import de.skuzzle.test.snapshots.ContextFiles;
10
import de.skuzzle.test.snapshots.Snapshot;
11
import de.skuzzle.test.snapshots.SnapshotTestResult;
12
import de.skuzzle.test.snapshots.impl.OrphanCollectorHolder.OrphanCollector;
13
import de.skuzzle.test.snapshots.io.DirectoryResolver;
14
import de.skuzzle.test.snapshots.validation.Arguments;
15

16
import org.apiguardian.api.API;
17
import org.apiguardian.api.API.Status;
18

19
/**
20
 * Context object that pertains to the execution of a whole test that uses snapshot test.
21
 * <p>
22
 * In order to initiate the run of a test class which uses snapshot assertions, you need
23
 * to meet two requirements:
24
 * <ol>
25
 * <li>You need to obtain a {@link SnapshotConfiguration} instance from the resp. test
26
 * class via {@link SnapshotConfiguration#defaultConfigurationFor(Class)}. You can then
27
 * create a {@link SnapshotTestContext} instance from that configuration.</li>
28
 * <li>You need to call the lifecycle methods of the context object.</li>
29
 * </ol>
30
 * Most aspects of snapshot testing will already work if you just call
31
 * {@link #finalizeSnapshotTest()} after a test method. However, in order to support full
32
 * orphan detection capabilities, you need to register both all ignored tests and all
33
 * failed tests of the current test execution.
34
 *
35
 * @author Simon Taddiken
36
 * @since 1.1.0
37
 */
38
@API(status = Status.INTERNAL, since = "1.1.0")
39
public final class SnapshotTestContext {
40

41
    private final DynamicOrphanedSnapshotsDetector dynamicOrphanedSnapshotsDetector = new DynamicOrphanedSnapshotsDetector();
1✔
42
    private final SnapshotConfiguration snapshotConfiguration;
43
    private final TestFrameworkSupport testFrameworkSupport;
44

45
    private SnapshotDslImpl currentSnapshotTest;
46

47
    private SnapshotTestContext(SnapshotConfiguration snapshotConfiguration,
48
            TestFrameworkSupport testFrameworkSupport) {
1✔
49
        this.snapshotConfiguration = Arguments.requireNonNull(snapshotConfiguration,
1✔
50
                "snapshotConfiguration must not be null");
51
        this.testFrameworkSupport = Arguments.requireNonNull(testFrameworkSupport,
1✔
52
                "testFrameworkSupport must not be null");
53
    }
1✔
54

55
    public static SnapshotTestContext forConfiguration(SnapshotConfiguration snapshotConfiguration,
56
            TestFrameworkSupport testFrameworkSupport) {
57
        return new SnapshotTestContext(snapshotConfiguration, testFrameworkSupport);
1✔
58
    }
59

60
    /**
61
     * Returns the {@link SnapshotConfiguration}.
62
     *
63
     * @return The configuration.
64
     * @since 1.9.0
65
     */
66
    @API(status = Status.INTERNAL, since = "1.9.0")
67
    public SnapshotConfiguration snapshotConfiguration() {
68
        return snapshotConfiguration;
1✔
69
    }
70

71
    /**
72
     * Returns the TestFrameworkSupport that encapsulates test framework specific
73
     * behavior.
74
     *
75
     * @return Thes {@link TestFrameworkSupport}.
76
     * @since 1.10.0
77
     */
78
    @API(status = Status.INTERNAL, since = "1.10.0")
79
    public TestFrameworkSupport testFrameworkSupport() {
80
        return testFrameworkSupport;
1✔
81
    }
82

83
    /**
84
     * Determines whether the parameters with the given type are eligible for injecting
85
     * the object that is created by {@link #createSnapshotTestFor(Method)}.
86
     *
87
     * @param type A type.
88
     * @return Whether the type of the object returned by
89
     *         {@link #createSnapshotTestFor(Method)} is compatible to the given type.
90
     */
91
    public boolean isSnapshotParameter(Class<?> type) {
92
        return Snapshot.class.isAssignableFrom(type);
1✔
93
    }
94

95
    /**
96
     * Creates a Snapshot object that can be injected into a test method as starting point
97
     * of the snapshot DSL.
98
     * <p>
99
     * This method changes the state of this context object. A new Snapshot can only be
100
     * created, when the current one has been retrieved and cleared using
101
     * {@link #finalizeSnapshotTest()}.
102
     *
103
     * @param testMethod The test method.
104
     * @return A Snapshot instance.
105
     * @see #finalizeSnapshotTest()
106
     */
107
    public de.skuzzle.test.snapshots.Snapshot createSnapshotTestFor(Method testMethod) {
108
        if (currentSnapshotTest != null) {
1✔
109
            throw new IllegalStateException("There is already a current snapshot test");
×
110
        }
111
        final ResultRecorder resultRecorder = ResultRecorder.forFreshTestMethod(this, testMethod);
1✔
112
        currentSnapshotTest = new SnapshotDslImpl(resultRecorder, snapshotConfiguration, testMethod);
1✔
113
        return currentSnapshotTest;
1✔
114
    }
115

116
    /**
117
     * Finalizes the current test by clearing this context and executing its remaining
118
     * assertions.
119
     *
120
     * @throws Exception If late assertions of the test fail.
121
     * @see #createSnapshotTestFor(Method)
122
     */
123
    public void finalizeSnapshotTest() throws Exception {
124
        final SnapshotDslImpl current = this.currentSnapshotTest;
1✔
125
        this.currentSnapshotTest = null;
1✔
126
        if (current != null) {
1✔
127
            current.executeFinalAssertions();
1✔
128
        }
129
    }
1✔
130

131
    /**
132
     * Records a failed or skipped test within the currently executed test class. Knowing
133
     * which tests did not complete successfully is crucial for implementing orphaned
134
     * snapshot detection.
135
     *
136
     * @param testMethod Test method that failed or has been skipped.
137
     */
138
    public void recordFailedOrSkippedTest(Method testMethod) {
139
        this.dynamicOrphanedSnapshotsDetector.addFailedOrSkippedTestMethod(testMethod);
1✔
140
    }
1✔
141

142
    /**
143
     * Records the results from all snapshot assertions within a single test method.
144
     *
145
     * @param result A snapshot test result of a single snapshot assertion.
146
     */
147
    void recordSnapshotTestResult(SnapshotTestResult result) {
148
        this.dynamicOrphanedSnapshotsDetector.addResult(result);
1✔
149
    }
1✔
150

151
    /**
152
     * Uses the collected context information to detect and optionally also clean up
153
     * orphaned snapshot files.
154
     *
155
     * @return The detected orphans.
156
     */
157
    public Collection<Path> detectOrCleanupOrphanedSnapshots() {
158
        final boolean deleteOrphaned = this.snapshotConfiguration.isDeleteOrphanedSnapshots();
1✔
159
        final Path globalSnapshotDirectory = this.snapshotConfiguration.determineSnapshotDirectory();
1✔
160

161
        // all orphan detection results will be reported with this collector. By default,
162
        // the collector doesn't do anything, but it can be exchanged with a mock
163
        // collector during unit tests to have easier access to the orphans.
164
        final OrphanCollector collector = OrphanCollectorHolder.getCollector();
1✔
165

166
        final Stream<OrphanDetectionResult> dynamicOrphans = dynamicOrphanedSnapshotsDetector
1✔
167
                .detectOrphans(globalSnapshotDirectory)
1✔
168
                .peek(collector::addRawResult);
1✔
169

170
        final Stream<OrphanDetectionResult> staticOrphans = new StaticOrphanedSnapshotDetector(testFrameworkSupport)
1✔
171
                .detectOrphans(DirectoryResolver.BASE)
1✔
172
                .peek(collector::addRawResult);
1✔
173

174
        return new OrphanPostProcessor()
1✔
175
                .orphanedOnly(Stream.concat(dynamicOrphans, staticOrphans).collect(Collectors.toList()))
1✔
176
                .peek(collector::addPostProcessedResult)
1✔
177
                .map(OrphanDetectionResult::snapshotFile)
1✔
178
                .distinct()
1✔
179
                .peek(orphanedSnapshotFile -> {
1✔
180

181
                    final Path relativePath = DirectoryResolver.relativize(orphanedSnapshotFile.getParent());
1✔
182
                    if (deleteOrphaned) {
1✔
183
                        final ContextFiles contextFiles = InternalSnapshotNaming
×
184
                                .contextFilesForSnapshotFile(orphanedSnapshotFile);
×
185
                        contextFiles.deleteFiles();
×
186

187
                        System.err.printf("Deleted orphaned snapshot file %s in %s%n",
×
188
                                orphanedSnapshotFile.getFileName(), relativePath);
×
189
                    } else {
×
190
                        System.err.printf(
1✔
191
                                "Found orphaned snapshot file. Run with '@DeleteOrphanedSnapshots' annotation to remove: %s in %s%n",
192
                                orphanedSnapshotFile.getFileName(), relativePath);
1✔
193
                    }
194
                })
1✔
195
                .collect(Collectors.toList());
1✔
196
    }
197

198
    @Override
199
    public String toString() {
200
        return "SnapshotTestContext[" + snapshotConfiguration + "]";
×
201
    }
202
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc