1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.api;
21  
22  import static com.puppycrawl.tools.checkstyle.utils.CommonUtil.EMPTY_OBJECT_ARRAY;
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertNotNull;
26  import static org.junit.Assert.assertNull;
27  import static org.junit.Assert.assertTrue;
28  
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.net.URL;
32  import java.net.URLConnection;
33  import java.net.URLStreamHandler;
34  import java.util.Locale;
35  import java.util.Map;
36  import java.util.ResourceBundle;
37  import java.util.concurrent.atomic.AtomicBoolean;
38  
39  import org.junit.After;
40  import org.junit.Test;
41  import org.powermock.reflect.Whitebox;
42  
43  import nl.jqno.equalsverifier.EqualsVerifier;
44  import nl.jqno.equalsverifier.EqualsVerifierReport;
45  
46  /**
47   * Custom class loader is needed to pass URLs to pretend these are loaded from the classpath
48   * though we can't add/change the files for testing. The class loader is nested in this class,
49   * so the custom class loader we are using is safe.
50   * @noinspection ClassLoaderInstantiation
51   */
52  public class LocalizedMessageTest {
53  
54      private static final Locale DEFAULT_LOCALE = Locale.getDefault();
55  
56      @Test
57      public void testEqualsAndHashCode() {
58          final EqualsVerifierReport ev = EqualsVerifier.forClass(LocalizedMessage.class)
59                  .usingGetClass().report();
60          assertEquals("Error: " + ev.getMessage(), EqualsVerifierReport.SUCCESS, ev);
61      }
62  
63      @Test
64      public void testGetSeverityLevel() {
65          final LocalizedMessage localizedMessage = createSampleLocalizedMessage();
66  
67          assertEquals("Invalid severity level", SeverityLevel.ERROR,
68                  localizedMessage.getSeverityLevel());
69      }
70  
71      @Test
72      public void testGetModuleId() {
73          final LocalizedMessage localizedMessage = createSampleLocalizedMessage();
74  
75          assertEquals("Invalid module id", "module", localizedMessage.getModuleId());
76      }
77  
78      @Test
79      public void testGetSourceName() {
80          final LocalizedMessage localizedMessage = createSampleLocalizedMessage();
81  
82          assertEquals("Invalid source name", "com.puppycrawl.tools.checkstyle.api.LocalizedMessage",
83                  localizedMessage.getSourceName());
84      }
85  
86      @Test
87      public void testMessageInEnglish() {
88          final LocalizedMessage localizedMessage = createSampleLocalizedMessage();
89          LocalizedMessage.setLocale(Locale.ENGLISH);
90  
91          assertEquals("Invalid message", "Empty statement.", localizedMessage.getMessage());
92      }
93  
94      @Test
95      public void testBundleReloadUrlNull() throws IOException {
96          final LocalizedMessage.Utf8Control control = new LocalizedMessage.Utf8Control();
97          final ResourceBundle bundle = control.newBundle(
98                  "com.puppycrawl.tools.checkstyle.checks.coding.messages",
99                  Locale.ENGLISH, "java.class",
100                 Thread.currentThread().getContextClassLoader(), true);
101         assertNull("Bundle should be null when reload is true and URL is null", bundle);
102     }
103 
104     /**
105      * Ignore resource errors for testing.
106      * @noinspection resource, IOResourceOpenedButNotSafelyClosed
107      */
108     @Test
109     public void testBundleReloadUrlNotNull() throws IOException {
110         final AtomicBoolean closed = new AtomicBoolean();
111 
112         final InputStream inputStream = new InputStream() {
113             @Override
114             public int read() {
115                 return -1;
116             }
117 
118             @Override
119             public void close() {
120                 closed.set(true);
121             }
122         };
123         final URLConnection urlConnection = new URLConnection(null) {
124             @Override
125             public void connect() {
126                 // no code
127             }
128 
129             @Override
130             public InputStream getInputStream() {
131                 return inputStream;
132             }
133         };
134         final URL url = new URL("test", null, 0, "", new URLStreamHandler() {
135             @Override
136             protected URLConnection openConnection(URL u) {
137                 return urlConnection;
138             }
139         });
140 
141         final LocalizedMessage.Utf8Control control = new LocalizedMessage.Utf8Control();
142         final ResourceBundle bundle = control.newBundle(
143                 "com.puppycrawl.tools.checkstyle.checks.coding.messages", Locale.ENGLISH,
144                 "java.class", new TestUrlsClassLoader(url), true);
145 
146         assertNotNull("Bundle should not be null when stream is not null", bundle);
147         assertFalse("connection should not be using caches", urlConnection.getUseCaches());
148         assertTrue("connection should be closed", closed.get());
149     }
150 
151     /**
152      * Ignore resource errors for testing.
153      * @noinspection resource, IOResourceOpenedButNotSafelyClosed
154      */
155     @Test
156     public void testBundleReloadUrlNotNullFalseReload() throws IOException {
157         final AtomicBoolean closed = new AtomicBoolean();
158 
159         final InputStream inputStream = new InputStream() {
160             @Override
161             public int read() {
162                 return -1;
163             }
164 
165             @Override
166             public void close() {
167                 closed.set(true);
168             }
169         };
170         final URLConnection urlConnection = new URLConnection(null) {
171             @Override
172             public void connect() {
173                 // no code
174             }
175 
176             @Override
177             public InputStream getInputStream() {
178                 return inputStream;
179             }
180         };
181         final URL url = new URL("test", null, 0, "", new URLStreamHandler() {
182             @Override
183             protected URLConnection openConnection(URL u) {
184                 return urlConnection;
185             }
186         });
187 
188         final LocalizedMessage.Utf8Control control = new LocalizedMessage.Utf8Control();
189         final ResourceBundle bundle = control.newBundle(
190                 "com.puppycrawl.tools.checkstyle.checks.coding.messages", Locale.ENGLISH,
191                 "java.class", new TestUrlsClassLoader(url), false);
192 
193         assertNotNull("Bundle should not be null when stream is not null", bundle);
194         assertTrue("connection should not be using caches", urlConnection.getUseCaches());
195         assertTrue("connection should be closed", closed.get());
196     }
197 
198     @Test
199     public void testBundleReloadUrlNotNullStreamNull() throws IOException {
200         final URL url = new URL("test", null, 0, "", new URLStreamHandler() {
201             @Override
202             protected URLConnection openConnection(URL u) {
203                 return null;
204             }
205         });
206 
207         final LocalizedMessage.Utf8Control control = new LocalizedMessage.Utf8Control();
208         final ResourceBundle bundle = control.newBundle(
209                 "com.puppycrawl.tools.checkstyle.checks.coding.messages",
210                 Locale.ENGLISH, "java.class",
211                 new TestUrlsClassLoader(url), true);
212         assertNull("Bundle should be null when stream is null", bundle);
213     }
214 
215     @Test
216     public void testMessageInFrench() {
217         final LocalizedMessage localizedMessage = createSampleLocalizedMessage();
218         LocalizedMessage.setLocale(Locale.FRENCH);
219 
220         assertEquals("Invalid message", "Instruction vide.", localizedMessage.getMessage());
221     }
222 
223     @Test
224     public void testEnforceEnglishLanguageBySettingUnitedStatesLocale() {
225         Locale.setDefault(Locale.FRENCH);
226         LocalizedMessage.setLocale(Locale.US);
227         final LocalizedMessage localizedMessage = createSampleLocalizedMessage();
228 
229         assertEquals("Invalid message", "Empty statement.", localizedMessage.getMessage());
230     }
231 
232     @Test
233     public void testEnforceEnglishLanguageBySettingRootLocale() {
234         Locale.setDefault(Locale.FRENCH);
235         LocalizedMessage.setLocale(Locale.ROOT);
236         final LocalizedMessage localizedMessage = createSampleLocalizedMessage();
237 
238         assertEquals("Invalid message", "Empty statement.", localizedMessage.getMessage());
239     }
240 
241     @Test
242     public void testGetKey() {
243         Locale.setDefault(Locale.FRENCH);
244         LocalizedMessage.setLocale(Locale.US);
245         final LocalizedMessage localizedMessage = createSampleLocalizedMessage();
246 
247         assertEquals("Invalid message key", "empty.statement", localizedMessage.getKey());
248     }
249 
250     @Test
251     public void testCleatBundleCache() {
252         Locale.setDefault(Locale.FRENCH);
253         LocalizedMessage.setLocale(Locale.ROOT);
254         final LocalizedMessage localizedMessage = createSampleLocalizedMessage();
255 
256         assertEquals("Invalid message", "Empty statement.", localizedMessage.getMessage());
257 
258         final Map<String, ResourceBundle> bundleCache =
259                 Whitebox.getInternalState(LocalizedMessage.class, "BUNDLE_CACHE");
260 
261         assertEquals("Invalid bundle cache size", 1, bundleCache.size());
262 
263         LocalizedMessage.setLocale(Locale.CHINA);
264 
265         assertEquals("Invalid bundle cache size", 0, bundleCache.size());
266     }
267 
268     @Test
269     public void testTokenType() {
270         final LocalizedMessage localizedMessage1 = new LocalizedMessage(1, 1, TokenTypes.CLASS_DEF,
271                 "messages.properties", "key", null, SeverityLevel.ERROR, null,
272                 getClass(), null);
273         final LocalizedMessage localizedMessage2 = new LocalizedMessage(1, 1, TokenTypes.OBJBLOCK,
274                 "messages.properties", "key", EMPTY_OBJECT_ARRAY, SeverityLevel.ERROR, null,
275                 getClass(), null);
276 
277         assertEquals("Invalid token type", TokenTypes.CLASS_DEF, localizedMessage1.getTokenType());
278         assertEquals("Invalid token type", TokenTypes.OBJBLOCK, localizedMessage2.getTokenType());
279     }
280 
281     @Test
282     public void testGetColumnCharIndex() {
283         final LocalizedMessage localizedMessage1 = new LocalizedMessage(1, 1, 123,
284                 TokenTypes.CLASS_DEF, "messages.properties", "key", null, SeverityLevel.ERROR,
285                 null, getClass(), null);
286 
287         assertEquals("Invalid column char index", 123, localizedMessage1.getColumnCharIndex());
288     }
289 
290     @Test
291     public void testCompareToWithDifferentModuleId() {
292         final LocalizedMessage message1 = createSampleLocalizedMessageWithId("module1");
293         final LocalizedMessage message2 = createSampleLocalizedMessageWithId("module2");
294         final LocalizedMessage messageNull = createSampleLocalizedMessageWithId(null);
295 
296         assertTrue("Invalid comparing result", message1.compareTo(messageNull) > 0);
297         assertTrue("Invalid comparing result", messageNull.compareTo(message1) < 0);
298         assertTrue("Invalid comparing result", message1.compareTo(message2) < 0);
299     }
300 
301     @Test
302     public void testCompareToWithDifferentLines() {
303         final LocalizedMessage message1 = createSampleLocalizedMessageWithLine(1);
304         final LocalizedMessage message1a = createSampleLocalizedMessageWithLine(1);
305         final LocalizedMessage message2 = createSampleLocalizedMessageWithLine(2);
306 
307         assertTrue("Invalid comparing result", message1.compareTo(message2) < 0);
308         assertTrue("Invalid comparing result", message2.compareTo(message1) > 0);
309         assertEquals("Invalid comparing result", 0, message1.compareTo(message1a));
310     }
311 
312     @Test
313     public void testCompareToWithDifferentColumns() {
314         final LocalizedMessage message1 = createSampleLocalizedMessageWithColumn(1);
315         final LocalizedMessage message1a = createSampleLocalizedMessageWithColumn(1);
316         final LocalizedMessage message2 = createSampleLocalizedMessageWithColumn(2);
317 
318         assertTrue("Invalid comparing result", message1.compareTo(message2) < 0);
319         assertTrue("Invalid comparing result", message2.compareTo(message1) > 0);
320         assertEquals("Invalid comparing result", 0, message1.compareTo(message1a));
321     }
322 
323     private static LocalizedMessage createSampleLocalizedMessage() {
324         return createSampleLocalizedMessageWithId("module");
325     }
326 
327     private static LocalizedMessage createSampleLocalizedMessageWithId(String id) {
328         return new LocalizedMessage(1, "com.puppycrawl.tools.checkstyle.checks.coding.messages",
329                 "empty.statement", EMPTY_OBJECT_ARRAY, id, LocalizedMessage.class, null);
330     }
331 
332     private static LocalizedMessage createSampleLocalizedMessageWithLine(int line) {
333         return new LocalizedMessage(line, "com.puppycrawl.tools.checkstyle.checks.coding.messages",
334                 "empty.statement", EMPTY_OBJECT_ARRAY, "module", LocalizedMessage.class, null);
335     }
336 
337     private static LocalizedMessage createSampleLocalizedMessageWithColumn(int column) {
338         return new LocalizedMessage(1, column,
339                 "com.puppycrawl.tools.checkstyle.checks.coding.messages", "empty.statement",
340                 EMPTY_OBJECT_ARRAY, "module", LocalizedMessage.class, null);
341     }
342 
343     @After
344     public void tearDown() {
345         Locale.setDefault(DEFAULT_LOCALE);
346         LocalizedMessage.clearCache();
347         LocalizedMessage.setLocale(DEFAULT_LOCALE);
348     }
349 
350     /**
351      * Custom class loader is needed to pass URLs to pretend these are loaded from the classpath
352      * though we can't add/change the files for testing.
353      * @noinspection CustomClassloader
354      */
355     private static class TestUrlsClassLoader extends ClassLoader {
356 
357         private final URL url;
358 
359         /* package */ TestUrlsClassLoader(URL url) {
360             this.url = url;
361         }
362 
363         @Override
364         public URL getResource(String name) {
365             return url;
366         }
367     }
368 
369 }