1 /* 2 * ************************************************************************************************************************************************************* 3 * 4 * TheseFoolishThings: Miscellaneous utilities 5 * http://tidalwave.it/projects/thesefoolishthings 6 * 7 * Copyright (C) 2009 - 2025 by Tidalwave s.a.s. (http://tidalwave.it) 8 * 9 * ************************************************************************************************************************************************************* 10 * 11 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 12 * You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 17 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 18 * 19 * ************************************************************************************************************************************************************* 20 * 21 * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src 22 * git clone https://github.com/tidalwave-it/thesefoolishthings-src 23 * 24 * ************************************************************************************************************************************************************* 25 */ 26 package it.tidalwave.util.test; 27 28 import jakarta.annotation.Nonnull; 29 import java.util.List; 30 import java.io.FileNotFoundException; 31 import java.io.IOException; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import lombok.RequiredArgsConstructor; 35 import lombok.extern.slf4j.Slf4j; 36 import static java.nio.charset.StandardCharsets.UTF_8; 37 import static it.tidalwave.util.test.FileComparisonUtils.assertSameContents; 38 import static lombok.AccessLevel.PRIVATE; 39 40 /*************************************************************************************************************************************************************** 41 * 42 * A facility that provides some common tasks for testing, such as manipulating test files. 43 * 44 * @author Fabrizio Giudici 45 * @since 3.2-ALPHA-18 46 * 47 **************************************************************************************************************************************************************/ 48 @RequiredArgsConstructor @Slf4j 49 public class BaseTestHelper 50 { 51 @Nonnull 52 protected final Object test; 53 54 /*********************************************************************************************************************************************************** 55 * Returns a {@link Path} for a resource file. The resource should be placed under 56 * {@code src/test/resources/test-class-simple-name/test-resources/resource-name}. Note that the file actually 57 * loaded is the one under {@code target/test-classes} copied there (and eventually filtered) by Maven. 58 * 59 * @param resourceName the resource name 60 * @return the {@code Path} 61 **********************************************************************************************************************************************************/ 62 @Nonnull 63 public Path resourceFileFor (@Nonnull final String resourceName) 64 { 65 final var testName = test.getClass().getSimpleName(); 66 return Path.of("target/test-classes", testName, "test-resources", resourceName); 67 } 68 69 /*********************************************************************************************************************************************************** 70 * Reads the content from the resource file as a single string. See {@link #resourceFileFor(String)} for 71 * further info. 72 * 73 * @param resourceName the resource name 74 * @return the string 75 * @throws IOException in case of error 76 **********************************************************************************************************************************************************/ 77 @Nonnull 78 public String readStringFromResource (@Nonnull final String resourceName) 79 throws IOException 80 { 81 final var file = resourceFileFor(resourceName); 82 final var buffer = new StringBuilder(); 83 var separator = ""; 84 85 for (final var string : Files.readAllLines(file, UTF_8)) 86 { 87 buffer.append(separator).append(string); 88 separator = "\n"; 89 } 90 91 return buffer.toString(); 92 // return String.join("\n", Files.readAllLines(path, UTF_8)); TODO JDK 8 93 } 94 95 /*********************************************************************************************************************************************************** 96 * Create a {@link TestResource} for the given name. The actual file will be created under 97 * {@code target/test-artifacts/test-class-simple-name/resourceName}. The expected file should be 98 * placed in {@code src/test/resources/test-class-simple-name/expected-results/resource-name}. Note that the file 99 * actually loaded is the one under {@code target/test-classes} copied there (and eventually filtered) by Maven. 100 * The {@code test-class-simple-name} is tried first with the current test, and then with its eventual 101 * super-classes; this allows to extend existing test suites. Note that if the resource files for a super class are 102 * not in the current project module, they should be explicitly copied here (for instance, by means of the 103 * Maven dependency plugin). 104 * 105 * @param resourceName the name 106 * @return the {@code TestResource} 107 * @throws IOException in case of error 108 **********************************************************************************************************************************************************/ 109 @Nonnull 110 public TestResource testResourceFor (@Nonnull final String resourceName) 111 throws IOException 112 { 113 final var testName = test.getClass().getSimpleName(); 114 final var expectedFile = findExpectedFilePath(resourceName); 115 final var actualFile = Path.of("target/test-artifacts", testName, resourceName); 116 Files.createDirectories(actualFile.getParent()); 117 return new TestResource(resourceName, actualFile, expectedFile); 118 } 119 120 /*********************************************************************************************************************************************************** 121 * 122 **********************************************************************************************************************************************************/ 123 @Nonnull 124 private Path findExpectedFilePath (@Nonnull final String resourceName) 125 throws IOException 126 { 127 for (var testClass = test.getClass(); testClass != null; testClass = testClass.getSuperclass()) 128 { 129 final var expectedFile = 130 Path.of("target/test-classes", testClass.getSimpleName(), "expected-results", resourceName); 131 132 if (Files.exists(expectedFile)) 133 { 134 return expectedFile; 135 } 136 } 137 138 throw new FileNotFoundException("Expected file for test " + resourceName); 139 } 140 141 /*********************************************************************************************************************************************************** 142 * A manipulator of a pair of (actual file, expected file). 143 **********************************************************************************************************************************************************/ 144 @RequiredArgsConstructor(access = PRIVATE) 145 public final class TestResource 146 { 147 @Nonnull 148 private final String name; 149 150 @Nonnull 151 private final Path actualFile; 152 153 @Nonnull 154 private final Path expectedFile; 155 156 /*************************************************************************************************************** 157 * 158 * Assert that the content of the actual file are the same as the expected file. 159 * 160 * @throws IOException in case of error 161 * 162 **************************************************************************************************************/ 163 public void assertActualFileContentSameAsExpected () 164 throws IOException 165 { 166 assertSameContents(expectedFile.toFile(), actualFile.toFile()); 167 } 168 169 /*************************************************************************************************************** 170 * 171 * Writes the given strings to the actual file. 172 * 173 * @param strings the strings 174 * @throws IOException in case of error 175 * 176 **************************************************************************************************************/ 177 public void writeToActualFile (@Nonnull final String... strings) 178 throws IOException 179 { 180 writeToActualFile(List.of(strings)); 181 } 182 183 /*************************************************************************************************************** 184 * 185 * Writes the given strings to the actual file. 186 * 187 * @param strings the strings 188 * @throws IOException in case of error 189 * 190 **************************************************************************************************************/ 191 public void writeToActualFile (@Nonnull final Iterable<String> strings) 192 throws IOException 193 { 194 Files.write(actualFile, strings, UTF_8); 195 } 196 197 /*************************************************************************************************************** 198 * 199 * Writes the given bytes to the actual file. 200 * 201 * @param bytes the bytes 202 * @throws IOException in case of error 203 * 204 **************************************************************************************************************/ 205 public void writeToActualFile (@Nonnull final byte[] bytes) 206 throws IOException 207 { 208 Files.write(actualFile, bytes); 209 } 210 211 /*************************************************************************************************************** 212 * 213 * Reads the content from the resource file as a single string. 214 * 215 * @return the string 216 * @throws IOException in case of error 217 * 218 **************************************************************************************************************/ 219 @Nonnull 220 public String readStringFromResource () 221 throws IOException 222 { 223 return BaseTestHelper.this.readStringFromResource(name); 224 } 225 } 226 }