View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static org.junit.jupiter.api.Assumptions.assumeFalse;
24  
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.net.URL;
28  import java.net.URLConnection;
29  import java.net.URLStreamHandler;
30  import java.util.Locale;
31  import java.util.ResourceBundle;
32  import java.util.concurrent.atomic.AtomicBoolean;
33  
34  import org.junit.jupiter.api.AfterEach;
35  import org.junit.jupiter.api.Test;
36  import org.junitpioneer.jupiter.DefaultLocale;
37  
38  import com.puppycrawl.tools.checkstyle.LocalizedMessage.Utf8Control;
39  
40  /**
41   * Custom class loader is needed to pass URLs to pretend these are loaded from the classpath
42   * though we can't add/change the files for testing. The class loader is nested in this class,
43   * so the custom class loader we are using is safe.
44   *
45   * @noinspection ClassLoaderInstantiation
46   * @noinspectionreason ClassLoaderInstantiation - Custom class loader is needed to
47   *      pass URLs for testing
48   */
49  public class LocalizedMessageTest {
50  
51      private static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
52  
53      @DefaultLocale("en")
54      @Test
55      public void testNullArgs() {
56          final LocalizedMessage messageClass = new LocalizedMessage(Definitions.CHECKSTYLE_BUNDLE,
57                  DefaultLogger.class, "DefaultLogger.addException", "myfile");
58          assertWithMessage("Violation should contain exception info")
59                  .that(messageClass.getMessage())
60                  .contains("Error auditing myfile");
61  
62          final LocalizedMessage nullClass = new LocalizedMessage(Definitions.CHECKSTYLE_BUNDLE,
63                  DefaultLogger.class, "DefaultLogger.addException");
64          final String outputForNullArgs = nullClass.getMessage();
65          assertWithMessage("Violation should contain exception info")
66                  .that(outputForNullArgs)
67                  .contains("Error auditing {0}");
68      }
69  
70      @Test
71      public void testBundleReloadUrlNull() throws IOException {
72          final Utf8Control control = new Utf8Control();
73          final ResourceBundle bundle = control.newBundle(
74                  "com.puppycrawl.tools.checkstyle.checks.coding.messages",
75                  Locale.ENGLISH, "java.class",
76                  Thread.currentThread().getContextClassLoader(), true);
77          assertWithMessage("Bundle should be null when reload is true and URL is null")
78                  .that(bundle)
79                  .isNull();
80      }
81  
82      /**
83       * Tests reload of resource bundle.
84       *
85       * @noinspection resource, IOResourceOpenedButNotSafelyClosed
86       * @noinspectionreason resource - we have no need to use try with resources in testing
87       * @noinspectionreason IOResourceOpenedButNotSafelyClosed - no need to close resources in
88       *      testing
89       */
90      @Test
91      public void testBundleReloadUrlNotNull() throws IOException {
92          final AtomicBoolean closed = new AtomicBoolean();
93  
94          final InputStream inputStream = new InputStream() {
95              @Override
96              public int read() {
97                  return -1;
98              }
99  
100             @Override
101             public void close() {
102                 closed.set(true);
103             }
104         };
105         final URLConnection urlConnection = new URLConnection(null) {
106             @Override
107             public void connect() {
108                 // no code
109             }
110 
111             @Override
112             public InputStream getInputStream() {
113                 return inputStream;
114             }
115         };
116         final URL url = new URL("test", null, 0, "", new URLStreamHandler() {
117             @Override
118             protected URLConnection openConnection(URL u) {
119                 return urlConnection;
120             }
121         });
122 
123         final Utf8Control control = new Utf8Control();
124         final ResourceBundle bundle = control.newBundle(
125                 "com.puppycrawl.tools.checkstyle.checks.coding.messages", Locale.ENGLISH,
126                 "java.class", new TestUrlsClassLoader(url), true);
127 
128         assertWithMessage("Bundle should not be null when stream is not null")
129                 .that(bundle)
130                 .isNotNull();
131         assertWithMessage("connection should not be using caches")
132                 .that(urlConnection.getUseCaches())
133                 .isFalse();
134         assertWithMessage("connection should be closed")
135                 .that(closed.get())
136                 .isTrue();
137     }
138 
139     /**
140      * Tests reload of resource bundle.
141      *
142      * @noinspection resource, IOResourceOpenedButNotSafelyClosed
143      * @noinspectionreason resource - we have no need to use try with resources in testing
144      * @noinspectionreason IOResourceOpenedButNotSafelyClosed - no need to close resources in
145      *      testing
146      */
147     @Test
148     public void testBundleReloadUrlNotNullFalseReload() throws IOException {
149         final AtomicBoolean closed = new AtomicBoolean();
150 
151         final InputStream inputStream = new InputStream() {
152             @Override
153             public int read() {
154                 return -1;
155             }
156 
157             @Override
158             public void close() {
159                 closed.set(true);
160             }
161         };
162         final URLConnection urlConnection = new URLConnection(null) {
163             @Override
164             public void connect() {
165                 // no code
166             }
167 
168             @Override
169             public InputStream getInputStream() {
170                 return inputStream;
171             }
172         };
173         final URL url = new URL("test", null, 0, "", new URLStreamHandler() {
174             @Override
175             protected URLConnection openConnection(URL u) {
176                 return urlConnection;
177             }
178         });
179 
180         final Utf8Control control = new Utf8Control();
181         final ResourceBundle bundle = control.newBundle(
182                 "com.puppycrawl.tools.checkstyle.checks.coding.messages", Locale.ENGLISH,
183                 "java.class", new TestUrlsClassLoader(url), false);
184 
185         assertWithMessage("Bundle should not be null when stream is not null")
186                 .that(bundle)
187                 .isNotNull();
188         assertWithMessage("connection should not be using caches")
189                 .that(urlConnection.getUseCaches())
190                 .isTrue();
191         assertWithMessage("connection should be closed")
192                 .that(closed.get())
193                 .isTrue();
194     }
195 
196     @Test
197     public void testBundleReloadUrlNotNullStreamNull() throws IOException {
198         final URL url = new URL("test", null, 0, "", new URLStreamHandler() {
199             @Override
200             protected URLConnection openConnection(URL ignore) {
201                 return null;
202             }
203         });
204 
205         final Utf8Control control = new Utf8Control();
206         final ResourceBundle bundle = control.newBundle(
207                 "com.puppycrawl.tools.checkstyle.checks.coding.messages",
208                 Locale.ENGLISH, "java.class",
209                 new TestUrlsClassLoader(url), true);
210         assertWithMessage("Bundle should be null when stream is null")
211                 .that(bundle)
212                 .isNull();
213     }
214 
215     /**
216      * Verifies that the language specified with the system property {@code user.language} exists.
217      */
218     @Test
219     public void testLanguageIsValid() {
220         final String language = DEFAULT_LOCALE.getLanguage();
221         assumeFalse(language.isEmpty(), "Locale not set");
222         assertWithMessage("Invalid language")
223                 .that(Locale.getISOLanguages())
224                 .asList()
225                 .contains(language);
226     }
227 
228     /**
229      * Verifies that the country specified with the system property {@code user.country} exists.
230      */
231     @Test
232     public void testCountryIsValid() {
233         final String country = DEFAULT_LOCALE.getCountry();
234         assumeFalse(country.isEmpty(), "Locale not set");
235         assertWithMessage("Invalid country")
236                 .that(Locale.getISOCountries())
237                 .asList()
238                 .contains(country);
239     }
240 
241     @Test
242     public void testMessageInFrench() {
243         final LocalizedMessage violation = createSampleViolation();
244         LocalizedMessage.setLocale(Locale.FRENCH);
245 
246         assertWithMessage("Invalid violation")
247             .that(violation.getMessage())
248             .isEqualTo("Instruction vide.");
249     }
250 
251     @DefaultLocale("fr")
252     @Test
253     public void testEnforceEnglishLanguageBySettingUnitedStatesLocale() {
254         LocalizedMessage.setLocale(Locale.US);
255         final LocalizedMessage violation = createSampleViolation();
256 
257         assertWithMessage("Invalid violation")
258             .that(violation.getMessage())
259             .isEqualTo("Empty statement.");
260     }
261 
262     @DefaultLocale("fr")
263     @Test
264     public void testEnforceEnglishLanguageBySettingRootLocale() {
265         LocalizedMessage.setLocale(Locale.ROOT);
266         final LocalizedMessage violation = createSampleViolation();
267 
268         assertWithMessage("Invalid violation")
269             .that(violation.getMessage())
270             .isEqualTo("Empty statement.");
271     }
272 
273     private static LocalizedMessage createSampleViolation() {
274         return new LocalizedMessage("com.puppycrawl.tools.checkstyle.checks.coding.messages",
275                 LocalizedMessage.class, "empty.statement");
276     }
277 
278     @AfterEach
279     public void tearDown() {
280         LocalizedMessage.setLocale(DEFAULT_LOCALE);
281     }
282 
283     /**
284      * Mocked ClassLoader for testing URL loading.
285      *
286      * @noinspection CustomClassloader
287      * @noinspectionreason CustomClassloader - needed to pass URLs to pretend these are loaded
288      *      from the classpath though we can't add/change the files for testing
289      */
290     private static final class TestUrlsClassLoader extends ClassLoader {
291 
292         private final URL url;
293 
294         private TestUrlsClassLoader(URL url) {
295             this.url = url;
296         }
297 
298         @Override
299         public URL getResource(String name) {
300             return url;
301         }
302     }
303 
304 }