1   /*
2   	StatCvs - CVS statistics generation 
3   	Copyright (C) 2002  Lukasz Pekacki <lukasz@pekacki.de>
4   	http://statcvs.sf.net/
5       
6   	This library is free software; you can redistribute it and/or
7   	modify it under the terms of the GNU Lesser General Public
8   	License as published by the Free Software Foundation; either
9   	version 2.1 of the License, or (at your option) any later version.
10  
11  	This library is distributed in the hope that it will be useful,
12  	but WITHOUT ANY WARRANTY; without even the implied warranty of
13  	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  	Lesser General Public License for more details.
15  
16  	You should have received a copy of the GNU Lesser General Public
17  	License along with this library; if not, write to the Free Software
18  	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20  package net.sf.statcvs.input;
21  
22  import java.io.InputStreamReader;
23  import java.io.Reader;
24  import java.io.StringReader;
25  import java.util.Calendar;
26  import java.util.Date;
27  import java.util.NoSuchElementException;
28  import java.util.TimeZone;
29  
30  import junit.framework.TestCase;
31  import net.sf.statcvs.util.LookaheadReader;
32  
33  /**
34   * Tests for {@link CvsLogfileParser} and {@link CvsFileBlockParser}. Most
35   * tests run the parser class on a logfile loaded from the file system and
36   * use a {@link MockBuilder} to verify the results.
37   * 
38   * @author Richard Cyganiak <richard@cyganiak.de>
39   * @version $Id: ParserTest.java,v 1.8 2009/08/20 17:44:05 benoitx Exp $
40   */
41  public class ParserTest extends TestCase {
42      private MockLogBuilder mock;
43      private RevisionData rev1;
44  
45      public ParserTest(final String arg0) {
46          super(arg0);
47      }
48  
49      /*
50       * @see TestCase#setUp()
51       */
52      protected void setUp() throws Exception {
53          super.setUp();
54          mock = new MockLogBuilder();
55          rev1 = new RevisionData();
56      }
57  
58      /**
59       * Tests simple.log
60       * @throws Exception on error
61       */
62      public void testSimpleLog() throws Exception {
63          mock.expectBuildModule("statcvs");
64          mock.expectBuildFile("LICENSE", false, false);
65          rev1.setRevisionNumber("1.1");
66          rev1.setDate(createDate(2003, 06, 04, 19, 32, 58));
67          rev1.setLoginName("cyganiak");
68          rev1.setStateExp();
69          rev1.setComment("renamed license.txt to LICENSE");
70          mock.expectBuildRevision(rev1);
71          parseLog("simple.log");
72          mock.verify();
73      }
74  
75      /**
76       * Tests a logfile which was created when uncommited files were present in
77       * the working copy ("? filename" lines at the beginning). They must be
78       * ignored.
79       * @throws Exception on error
80       */
81      public void testUncommittedFiles() throws Exception {
82          mock.expectBuildModule("statcvs");
83          mock.expectBuildFile("LICENSE", false, false);
84          mock.expectCurrentRevisionNumber("1.1");
85          parseLog("uncommitted-files.log");
86          mock.verify();
87      }
88  
89      /**
90       * Tests two-files.log
91       * @throws Exception
92       */
93      public void testTwoFiles() throws Exception {
94          mock.expectBuildModule("statcvs");
95          mock.expectBuildFile("LICENSE", false, false);
96          mock.expectCurrentRevisionNumber("1.2");
97          mock.expectNextRevision();
98          mock.expectCurrentRevisionNumber("1.1");
99          mock.expectBuildFile("README", false, false);
100         mock.expectCurrentRevisionNumber("1.1");
101         parseLog("two-files.log");
102         mock.verify();
103     }
104 
105     /**
106      * Tests parsing a log with a file that has no selected revisions.
107      * Necessary when specifying ranges of dates or tags. The log still
108      * contains all files but some may have no selected revisions.
109      * @throws Exception
110      */
111     public void testNoRevisionsSelected() throws Exception {
112         mock.expectBuildModule("statcvs");
113         mock.expectBuildFile("LICENSE", false, false);
114         mock.expectBuildFile("README", false, false);
115         mock.expectCurrentRevisionNumber("1.1");
116         parseLog("no-revs-selected.log");
117         mock.verify();
118     }
119 
120     /**
121      * Same as {@link #testNoRevisionsSelected}, but now the file has a description.
122      * The description must be ignored by the parser.
123      * @throws Exception
124      */
125     public void testNoRevisionsSelectedWithDescription() throws Exception {
126         mock.expectBuildModule("statcvs");
127         mock.expectBuildFile("LICENSE", false, false);
128         mock.expectBuildFile("README", false, false);
129         mock.expectCurrentRevisionNumber("1.1");
130         parseLog("no-revs-selected-w-description.log");
131         mock.verify();
132     }
133 
134     /**
135      * Not sure why that was put in, but apparently we want the parser to
136      * deal gracefully with newlines at the end of the file.
137      * @throws Exception on error
138      */
139     public void testEmptyLinesAfterEnd() throws Exception {
140         mock.expectBuildModule("statcvs");
141         mock.expectBuildFile("LICENSE", false, false);
142         mock.expectNextRevision();
143         parseLog("newlines-after-end.log");
144         mock.verify();
145     }
146 
147     /**
148      * Tests if the parser can handle a revision delimiter in the comment.
149      * @throws Exception
150      */
151     public void testRevisionDelimiterInComment() throws Exception {
152         mock.expectBuildModule("statcvs");
153         mock.expectBuildFile("LICENSE", false, false);
154         mock.expectCurrentRevisionNumber("1.1");
155         mock.expectCurrentComment("comment\n----------------------------\ncomment");
156         mock.expectBuildFile("README", false, false);
157         mock.expectCurrentRevisionNumber("1.1");
158         parseLog("delimiter-in-comment.log");
159         mock.verify();
160     }
161 
162     /**
163      * Tests for exception on empty logfile 
164      * @throws Exception
165      */
166     public void testEmptyLog() throws Exception {
167         final Reader reader = new StringReader("");
168         final CvsLogfileParser parser = new CvsLogfileParser(reader, mock);
169         parser.parse();
170         mock.verify();
171     }
172 
173     /**
174      * Tests for exception on bogus logfile 
175      * @throws Exception
176      */
177     public void testBogusLog() throws Exception {
178         final Reader reader = new StringReader("foo\nbar");
179         final CvsLogfileParser parser = new CvsLogfileParser(reader, mock);
180         try {
181             parser.parse();
182             fail("should have thrown LogSyntaxException");
183         } catch (final LogSyntaxException expected) {
184             // expected
185         }
186     }
187 
188     /**
189      * Tests the CvsFileBlockParser for a first file
190      * @throws Exception
191      */
192     public void testFirstFile() throws Exception {
193         mock.expectBuildModule("statcvs");
194         mock.expectBuildFile("LICENSE", false, false);
195         mock.expectCurrentRevisionNumber("1.1");
196         final Reader reader = new InputStreamReader(getClass().getResourceAsStream("simple.log2"));
197         final LookaheadReader lookahead = new LookaheadReader(reader);
198         lookahead.nextLine();
199         new CvsFileBlockParser(lookahead, mock, true).parse();
200         mock.verify();
201     }
202 
203     /**
204      * Tests the CvsFileBlockParser for a non-first file
205      * @throws Exception
206      */
207     public void testNonFirstFile() throws Exception {
208         mock.expectBuildFile("LICENSE", false, false);
209         mock.expectCurrentRevisionNumber("1.1");
210         parseOneFile("simple.log2");
211         mock.verify();
212     }
213 
214     /**
215      * Tests the CvsFileBlockParser for a file with description
216      * @throws Exception
217      */
218     public void testDescription() throws Exception {
219         mock.expectBuildFile("LICENSE", false, false);
220         mock.expectCurrentRevisionNumber("1.1");
221         parseOneFile("description.log2");
222         mock.verify();
223     }
224 
225     /**
226      * Tests parsing a log with a lock ("cyganiak: 1.1")
227      * @throws Exception on error
228      */
229     public void testLocks() throws Exception {
230         mock.expectBuildFile("LICENSE", false, false);
231         mock.expectCurrentRevisionNumber("1.1");
232         parseOneFile("locks.log2");
233         mock.verify();
234     }
235 
236     /**
237      * Tests the CvsFileBlockParser for a file with access list
238      * @throws Exception on error
239      */
240     public void testAccessList() throws Exception {
241         mock.expectBuildFile("LICENSE", false, false);
242         mock.expectCurrentRevisionNumber("1.1");
243         parseOneFile("access-list.log2");
244         mock.verify();
245     }
246 
247     /**
248      * Test log with missing "symbolic names:" section. Such
249      * logs are created by the -N switch of the cvs log command. 
250      * @throws Exception on error
251      */
252     public void testNoSymbolicNames() throws Exception {
253         mock.expectBuildFile("LICENSE", false, false);
254         mock.expectCurrentRevisionNumber("1.1");
255         parseOneFile("no-symbolic-names.log2");
256         mock.verify();
257     }
258 
259     /**
260      * Tests if attic files are correctly identified. 
261      * @throws Exception on error
262      * @see net.sf.statcvs.util.CvsLogUtilsTest.testIsInAttic
263      */
264     public void testIsInAttic() throws Exception {
265         mock.expectBuildFile("LICENSE", false, true);
266         mock.expectCurrentRevisionNumber("1.1");
267         parseOneFile("in-attic.log2");
268         mock.verify();
269     }
270 
271     public void testTwoRevisions() throws Exception {
272         mock.expectBuildFile("LICENSE", false, false);
273         mock.expectCurrentRevisionNumber("1.2");
274         mock.expectCurrentAuthor("jentzsch");
275         mock.expectCurrentDate(createDate(2003, 6, 5, 19, 32, 58));
276         mock.expectCurrentComment("comment2");
277         mock.expectCurrentStateExp();
278         mock.expectCurrentLines(10, 0);
279         mock.expectNextRevision();
280         mock.expectCurrentRevisionNumber("1.1");
281         mock.expectCurrentAuthor("cyganiak");
282         mock.expectCurrentDate(createDate(2003, 6, 4, 19, 32, 58));
283         mock.expectCurrentComment("comment1");
284         mock.expectCurrentNoLines();
285         parseOneFile("two-revisions.log2");
286         mock.verify();
287     }
288 
289     /**
290      * Tests the CvsFileBlockParser for a binary file
291      * @throws Exception
292      */
293     public void testBinary() throws Exception {
294         mock.expectBuildFile("LICENSE", true, false);
295         mock.expectCurrentRevisionNumber("1.1");
296         parseOneFile("binary.log2");
297         mock.verify();
298     }
299 
300     /**
301      * Recent CVS versions have a different date format
302      */
303     public void testNewCVSDates() throws Exception {
304         this.mock.expectBuildFile("LICENSE", false, false);
305         this.mock.expectCurrentRevisionNumber("1.1");
306         this.mock.expectCurrentDate(createDate(2004, 07, 18, 17, 42, 25));
307         parseOneFile("newdate.log2");
308         this.mock.verify();
309     }
310 
311     public void testPrematurelyEndingLog() throws Exception {
312         this.mock.expectBuildFile("LICENSE", false, false);
313         try {
314             parseOneFile("premature-end.log2");
315             fail();
316         } catch (final NoSuchElementException ex) {
317             // is expected because log ends right within a revision
318         }
319     }
320 
321     private void parseLog(final String name) throws Exception {
322         final Reader reader = new InputStreamReader(getClass().getResourceAsStream(name));
323         new CvsLogfileParser(reader, mock).parse();
324     }
325 
326     private void parseOneFile(final String name) throws Exception {
327         final Reader reader = new InputStreamReader(getClass().getResourceAsStream(name));
328         final LookaheadReader lookahead = new LookaheadReader(reader);
329         lookahead.nextLine();
330         new CvsFileBlockParser(lookahead, mock, false).parse();
331     }
332 
333     private Date createDate(final int year, final int month, final int day, final int hour, final int minute, final int second) {
334         final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
335         calendar.set(year, month - 1, day, hour, minute, second);
336         return calendar.getTime();
337     }
338 }