View Javadoc
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 java.nio.file.Paths;
35  import lombok.RequiredArgsConstructor;
36  import lombok.extern.slf4j.Slf4j;
37  import static java.nio.charset.StandardCharsets.UTF_8;
38  import static it.tidalwave.util.test.FileComparisonUtils.assertSameContents;
39  import static lombok.AccessLevel.PRIVATE;
40  
41  /***************************************************************************************************************************************************************
42   *
43   * A facility that provides some common tasks for testing, such as manipulating test files.
44   *
45   * @author  Fabrizio Giudici
46   * @since 3.2-ALPHA-18
47   *
48   **************************************************************************************************************************************************************/
49  @RequiredArgsConstructor @Slf4j
50  public class BaseTestHelper
51    {
52      @Nonnull
53      protected final Object test;
54  
55      /***********************************************************************************************************************************************************
56       * Returns a {@link Path} for a resource file. The resource should be placed under
57       * {@code src/test/resources/test-class-simple-name/test-resources/resource-name}. Note that the file actually
58       * loaded is the one under {@code target/test-classes} copied there (and eventually filtered) by Maven.
59       *
60       * @param   resourceName    the resource name
61       * @return the {@code Path}
62       **********************************************************************************************************************************************************/
63      @Nonnull
64      public Path resourceFileFor (@Nonnull final String resourceName)
65        {
66          final var testName = test.getClass().getSimpleName();
67          return Paths.get("target/test-classes", testName, "test-resources", resourceName);
68        }
69  
70      /***********************************************************************************************************************************************************
71       * Reads the content from the resource file as a single string. See {@link #resourceFileFor(String)} for
72       * further info.
73       *
74       * @param   resourceName    the resource name
75       * @return the string
76       * @throws IOException     in case of error
77       **********************************************************************************************************************************************************/
78      @Nonnull
79      public String readStringFromResource (@Nonnull final String resourceName)
80              throws IOException
81        {
82          final var file = resourceFileFor(resourceName);
83          final var buffer = new StringBuilder();
84          var separator = "";
85  
86          for (final var string : Files.readAllLines(file, UTF_8))
87            {
88              buffer.append(separator).append(string);
89              separator = "\n";
90            }
91  
92          return buffer.toString();
93  //            return String.join("\n", Files.readAllLines(path, UTF_8)); TODO JDK 8
94        }
95  
96      /***********************************************************************************************************************************************************
97       * Create a {@link TestResource} for the given name. The actual file will be created under
98       * {@code target/test-artifacts/test-class-simple-name/resourceName}. The expected file should be
99       * placed in {@code src/test/resources/test-class-simple-name/expected-results/resource-name}. Note that the file
100      * actually loaded is the one under {@code target/test-classes} copied there (and eventually filtered) by Maven.
101      * The {@code test-class-simple-name} is tried first with the current test, and then with its eventual
102      * super-classes; this allows to extend existing test suites. Note that if the resource files for a super class are
103      * not in the current project module, they should be explicitly copied here (for instance, by means of the
104      * Maven dependency plugin).
105      *
106      * @param   resourceName    the name
107      * @return the {@code TestResource}
108      * @throws IOException     in case of error
109      **********************************************************************************************************************************************************/
110     @Nonnull
111     public TestResource testResourceFor (@Nonnull final String resourceName)
112             throws IOException
113       {
114         final var testName = test.getClass().getSimpleName();
115         final var expectedFile = findExpectedFilePath(resourceName);
116         final var actualFile = Paths.get("target/test-artifacts", testName, resourceName);
117         Files.createDirectories(actualFile.getParent());
118         return new TestResource(resourceName, actualFile, expectedFile);
119       }
120 
121     /***********************************************************************************************************************************************************
122      * 
123      **********************************************************************************************************************************************************/
124     @Nonnull
125     private Path findExpectedFilePath (@Nonnull final String resourceName)
126             throws IOException
127       {
128         for (var testClass = test.getClass(); testClass != null; testClass = testClass.getSuperclass())
129           {
130             final var expectedFile =
131                     Paths.get("target/test-classes", testClass.getSimpleName(), "expected-results", resourceName);
132 
133             if (Files.exists(expectedFile))
134               {
135                 return expectedFile;
136               }
137           }
138 
139         throw new FileNotFoundException("Expected file for test " + resourceName);
140       }
141 
142     /***********************************************************************************************************************************************************
143      * A manipulator of a pair of (actual file, expected file).
144      **********************************************************************************************************************************************************/
145     @RequiredArgsConstructor(access = PRIVATE)
146     public final class TestResource
147       {
148         @Nonnull
149         private final String name;
150 
151         @Nonnull
152         private final Path actualFile;
153 
154         @Nonnull
155         private final Path expectedFile;
156 
157         /***************************************************************************************************************
158          *
159          * Assert that the content of the actual file are the same as the expected file.
160          *
161          * @throws IOException     in case of error
162          *
163          **************************************************************************************************************/
164         public void assertActualFileContentSameAsExpected ()
165                 throws IOException
166           {
167             assertSameContents(expectedFile.toFile(), actualFile.toFile());
168           }
169 
170         /***************************************************************************************************************
171          *
172          * Writes the given strings to the actual file.
173          *
174          * @param   strings         the strings
175          * @throws IOException     in case of error
176          *
177          **************************************************************************************************************/
178         public void writeToActualFile (@Nonnull final String... strings)
179                 throws IOException
180           {
181             writeToActualFile(List.of(strings));
182           }
183 
184         /***************************************************************************************************************
185          *
186          * Writes the given strings to the actual file.
187          *
188          * @param   strings         the strings
189          * @throws IOException     in case of error
190          *
191          **************************************************************************************************************/
192         public void writeToActualFile (@Nonnull final Iterable<String> strings)
193                 throws IOException
194           {
195             Files.write(actualFile, strings, UTF_8);
196           }
197 
198         /***************************************************************************************************************
199          *
200          * Writes the given bytes to the actual file.
201          *
202          * @param   bytes           the bytes
203          * @throws IOException     in case of error
204          *
205          **************************************************************************************************************/
206         public void writeToActualFile (@Nonnull final byte[] bytes)
207                 throws IOException
208           {
209             Files.write(actualFile, bytes);
210           }
211 
212         /***************************************************************************************************************
213          *
214          * Reads the content from the resource file as a single string.
215          *
216          * @return the string
217          * @throws IOException     in case of error
218          *
219          **************************************************************************************************************/
220         @Nonnull
221         public String readStringFromResource ()
222                 throws IOException
223           {
224             return BaseTestHelper.this.readStringFromResource(name);
225           }
226       }
227   }