1a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey/*
2a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * Copyright (C) 2012 The Android Open Source Project
3a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey *
4a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * you may not use this file except in compliance with the License.
6a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * You may obtain a copy of the License at
7a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey *
8a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey *
10a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
11a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * See the License for the specific language governing permissions and
14a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * limitations under the License.
15a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey */
16a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
17a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeypackage com.android.internal.util;
18a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
19a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport static android.text.format.DateUtils.DAY_IN_MILLIS;
20a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport static android.text.format.DateUtils.HOUR_IN_MILLIS;
21a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport static android.text.format.DateUtils.MINUTE_IN_MILLIS;
22a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport static android.text.format.DateUtils.SECOND_IN_MILLIS;
23a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport static android.text.format.DateUtils.WEEK_IN_MILLIS;
24a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport static android.text.format.DateUtils.YEAR_IN_MILLIS;
25a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
26a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport android.test.AndroidTestCase;
27a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport android.test.suitebuilder.annotation.Suppress;
28a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport android.util.Log;
29a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
30a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport com.android.internal.util.FileRotator.Reader;
31a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport com.android.internal.util.FileRotator.Writer;
32a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport com.google.android.collect.Lists;
33a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
34a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.io.DataInputStream;
35a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.io.DataOutputStream;
36a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.io.File;
37a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.io.FileOutputStream;
38a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.io.IOException;
39a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.io.InputStream;
40a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.io.OutputStream;
41a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.net.ProtocolException;
42a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.util.ArrayList;
43a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.util.Arrays;
44a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport java.util.Random;
45a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
46bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrandimport junit.framework.Assert;
47bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand
48a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeyimport libcore.io.IoUtils;
49a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
50a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey/**
51a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey * Tests for {@link FileRotator}.
52a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey */
53a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkeypublic class FileRotatorTest extends AndroidTestCase {
54a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static final String TAG = "FileRotatorTest";
55a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
56a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private File mBasePath;
57a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
58a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static final String PREFIX = "rotator";
59a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static final String ANOTHER_PREFIX = "another_rotator";
60a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
61a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static final long TEST_TIME = 1300000000000L;
62a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
63a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    // TODO: test throwing rolls back correctly
64a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
65a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    @Override
66a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    protected void setUp() throws Exception {
67a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        super.setUp();
68a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
69a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        mBasePath = getContext().getFilesDir();
70a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        IoUtils.deleteContents(mBasePath);
71a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
72a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
73a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testEmpty() throws Exception {
74a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate1 = new FileRotator(
75a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, DAY_IN_MILLIS, WEEK_IN_MILLIS);
76a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate2 = new FileRotator(
77a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, ANOTHER_PREFIX, DAY_IN_MILLIS, WEEK_IN_MILLIS);
78a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
79a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final RecordingReader reader = new RecordingReader();
80a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        long currentTime = TEST_TIME;
81a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
82a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // write single new value
83a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate1.combineActive(reader, writer("foo"), currentTime);
84a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead();
85a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
86a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // assert that one rotator doesn't leak into another
87a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate1, "foo");
88a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate2);
89a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
90a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
91a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testCombine() throws Exception {
92a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate = new FileRotator(
93a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, DAY_IN_MILLIS, WEEK_IN_MILLIS);
94a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
95a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final RecordingReader reader = new RecordingReader();
96a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        long currentTime = TEST_TIME;
97a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
98a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // first combine should have empty read, but still write data.
99a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("foo"), currentTime);
100a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead();
101a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "foo");
102a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
103a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // second combine should replace contents; should read existing data,
104a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // and write final data to disk.
105a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime += SECOND_IN_MILLIS;
106a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.reset();
107a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("bar"), currentTime);
108a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead("foo");
109a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "bar");
110a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
111a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
112a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testRotate() throws Exception {
113a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate = new FileRotator(
114a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, DAY_IN_MILLIS, WEEK_IN_MILLIS);
115a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
116a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final RecordingReader reader = new RecordingReader();
117a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        long currentTime = TEST_TIME;
118a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
119a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // combine first record into file
120a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("foo"), currentTime);
121a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead();
122a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "foo");
123a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
124a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // push time a few minutes forward; shouldn't rotate file
125a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.reset();
126a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime += MINUTE_IN_MILLIS;
127a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("bar"), currentTime);
128a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead("foo");
129a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "bar");
130a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
131a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // push time forward enough to rotate file; should still have same data
132a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime += DAY_IN_MILLIS + SECOND_IN_MILLIS;
133a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
134a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "bar");
135a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
136a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // combine a second time, should leave rotated value untouched, and
137a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // active file should be empty.
138a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.reset();
139a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("baz"), currentTime);
140a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead();
141a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "bar", "baz");
142a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
143a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
144a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testDelete() throws Exception {
145a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate = new FileRotator(
146a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, MINUTE_IN_MILLIS, DAY_IN_MILLIS);
147a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
148a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final RecordingReader reader = new RecordingReader();
149a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        long currentTime = TEST_TIME;
150a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
151a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // create first record and trigger rotating it
152a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("foo"), currentTime);
153a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead();
154a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime += MINUTE_IN_MILLIS + SECOND_IN_MILLIS;
155a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
156a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
157a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // create second record
158a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.reset();
159a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("bar"), currentTime);
160a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead();
161a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "foo", "bar");
162a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
163a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // push time far enough to expire first record
164a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime = TEST_TIME + DAY_IN_MILLIS + (2 * MINUTE_IN_MILLIS);
165a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
166a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "bar");
167a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
168a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // push further to delete second record
169a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime += WEEK_IN_MILLIS;
170a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
171a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate);
172a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
173a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
174a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testThrowRestoresBackup() throws Exception {
175a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate = new FileRotator(
176a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, MINUTE_IN_MILLIS, DAY_IN_MILLIS);
177a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
178a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final RecordingReader reader = new RecordingReader();
179a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        long currentTime = TEST_TIME;
180a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
181a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // first, write some valid data
182a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("foo"), currentTime);
183a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead();
184a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "foo");
185a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
186a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        try {
187a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            // now, try writing which will throw
188a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            reader.reset();
189a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            rotate.combineActive(reader, new Writer() {
190a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                public void write(OutputStream out) throws IOException {
191a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                    new DataOutputStream(out).writeUTF("bar");
1926de357e4d10fa5977ab9a6c665dc858765e95d34Jeff Sharkey                    throw new NullPointerException("yikes");
193a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                }
194a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            }, currentTime);
195a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
196a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            fail("woah, somehow able to write exception");
1976de357e4d10fa5977ab9a6c665dc858765e95d34Jeff Sharkey        } catch (IOException e) {
198a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            // expected from above
199a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        }
200a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
201a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // assert that we read original data, and that it's still intact after
202a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // the failed write above.
203a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead("foo");
204a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "foo");
205a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
206a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
207a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testOtherFilesAndMalformed() throws Exception {
208a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate = new FileRotator(
209a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, SECOND_IN_MILLIS, SECOND_IN_MILLIS);
210a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
211a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // should ignore another prefix
212a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        touch("another_rotator.1024");
213a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        touch("another_rotator.1024-2048");
214a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate);
215a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
216a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // verify that broken filenames don't crash
217a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        touch("rotator");
218a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        touch("rotator...");
219a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        touch("rotator.-");
220a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        touch("rotator.---");
221a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        touch("rotator.a-b");
222a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        touch("rotator_but_not_actually");
223a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate);
224a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
225a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // and make sure that we can read something from a legit file
226a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        write("rotator.100-200", "meow");
227a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "meow");
228a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
229a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
230a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static final String RED = "red";
231a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static final String GREEN = "green";
232a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static final String BLUE = "blue";
233a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static final String YELLOW = "yellow";
234a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
235a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testQueryMatch() throws Exception {
236a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate = new FileRotator(
237a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, HOUR_IN_MILLIS, YEAR_IN_MILLIS);
238a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
239a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final RecordingReader reader = new RecordingReader();
240a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        long currentTime = TEST_TIME;
241a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
242a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // rotate a bunch of historical data
243a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
244a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer(RED), currentTime);
245a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
246a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime += DAY_IN_MILLIS;
247a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
248a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer(GREEN), currentTime);
249a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
250a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime += DAY_IN_MILLIS;
251a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
252a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer(BLUE), currentTime);
253a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
254a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime += DAY_IN_MILLIS;
255a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
256a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer(YELLOW), currentTime);
257a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
258a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final String[] FULL_SET = { RED, GREEN, BLUE, YELLOW };
259a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
260a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, FULL_SET);
261a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadMatching(rotate, Long.MIN_VALUE, Long.MAX_VALUE, FULL_SET);
262a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadMatching(rotate, Long.MIN_VALUE, currentTime, FULL_SET);
263a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadMatching(rotate, TEST_TIME + SECOND_IN_MILLIS, currentTime, FULL_SET);
264a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
265a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // should omit last value, since it only touches at currentTime
266a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadMatching(rotate, TEST_TIME + SECOND_IN_MILLIS, currentTime - SECOND_IN_MILLIS,
267a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                RED, GREEN, BLUE);
268a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
269a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // check boundary condition
270a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadMatching(rotate, TEST_TIME + DAY_IN_MILLIS, Long.MAX_VALUE, FULL_SET);
271a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadMatching(rotate, TEST_TIME + DAY_IN_MILLIS + SECOND_IN_MILLIS, Long.MAX_VALUE,
272a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                GREEN, BLUE, YELLOW);
273a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
274a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // test range smaller than file
275a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final long blueStart = TEST_TIME + (DAY_IN_MILLIS * 2);
276a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final long blueEnd = TEST_TIME + (DAY_IN_MILLIS * 3);
277a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadMatching(rotate, blueStart + SECOND_IN_MILLIS, blueEnd - SECOND_IN_MILLIS, BLUE);
278a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
279a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // outside range should return nothing
280a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadMatching(rotate, Long.MIN_VALUE, TEST_TIME - DAY_IN_MILLIS);
281a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
282a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
283a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testClockRollingBackwards() throws Exception {
284a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate = new FileRotator(
285a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, DAY_IN_MILLIS, YEAR_IN_MILLIS);
286a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
287a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final RecordingReader reader = new RecordingReader();
288a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        long currentTime = TEST_TIME;
289a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
290a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // create record at current time
291a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // --> foo
292a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("foo"), currentTime);
293a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead();
294a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "foo");
295a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
296a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // record a day in past; should create a new active file
297a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // --> bar
298a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime -= DAY_IN_MILLIS;
299a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.reset();
300a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("bar"), currentTime);
301a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead();
302a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "bar", "foo");
303a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
304a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // verify that we rewrite current active file
305a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // bar --> baz
306a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime += SECOND_IN_MILLIS;
307a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.reset();
308a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("baz"), currentTime);
309a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead("bar");
310a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "baz", "foo");
311a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
312a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // return to present and verify we write oldest active file
313a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // baz --> meow
314a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        currentTime = TEST_TIME + SECOND_IN_MILLIS;
315a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.reset();
316a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("meow"), currentTime);
317a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead("baz");
318a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "meow", "foo");
319a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
320a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // current time should trigger rotate of older active file
321a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
322a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
323a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // write active file, verify this time we touch original
324a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // foo --> yay
325a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.reset();
326a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.combineActive(reader, writer("yay"), currentTime);
327a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead("foo");
328a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "meow", "yay");
329a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
330a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
331a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    @Suppress
332a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testFuzz() throws Exception {
333a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate = new FileRotator(
334a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, HOUR_IN_MILLIS, DAY_IN_MILLIS);
335a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
336a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final RecordingReader reader = new RecordingReader();
337a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        long currentTime = TEST_TIME;
338a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
339a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // walk forward through time, ensuring that files are cleaned properly
340a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final Random random = new Random();
341a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        for (int i = 0; i < 1024; i++) {
342a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            currentTime += Math.abs(random.nextLong()) % DAY_IN_MILLIS;
343a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
344a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            reader.reset();
345a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            rotate.combineActive(reader, writer("meow"), currentTime);
346a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
347a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            if (random.nextBoolean()) {
348a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                rotate.maybeRotate(currentTime);
349a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            }
350a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        }
351a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
352a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.maybeRotate(currentTime);
353a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
354a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        Log.d(TAG, "currentTime=" + currentTime);
355a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        Log.d(TAG, Arrays.toString(mBasePath.list()));
356a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
357a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
358a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    public void testRecoverAtomic() throws Exception {
359a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        write("rotator.1024-2048", "foo");
360a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        write("rotator.1024-2048.backup", "bar");
361a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        write("rotator.2048-4096", "baz");
362a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        write("rotator.2048-4096.no_backup", "");
363a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
364a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final FileRotator rotate = new FileRotator(
365a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                mBasePath, PREFIX, SECOND_IN_MILLIS, SECOND_IN_MILLIS);
366a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
367a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // verify backup value was recovered; no_backup indicates that
368a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        // corresponding file had no backup and should be discarded.
369a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadAll(rotate, "bar");
370a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
371a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
372bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand    public void testFileSystemInaccessible() throws Exception {
373bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand        File inaccessibleDir = null;
374bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand        String dirPath = getContext().getFilesDir() + File.separator + "inaccessible";
375bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand        inaccessibleDir = new File(dirPath);
376bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand        final FileRotator rotate = new FileRotator(inaccessibleDir, PREFIX, SECOND_IN_MILLIS, SECOND_IN_MILLIS);
377bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand
378bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand        // rotate should not throw on dir not mkdir-ed (or otherwise inaccessible)
379bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand        rotate.maybeRotate(TEST_TIME);
380bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand    }
381bbf1861fdd2ba250354c060fe70f0ee7cbe93877Mikael Gullstrand
382a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private void touch(String... names) throws IOException {
383a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        for (String name : names) {
384a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            final OutputStream out = new FileOutputStream(new File(mBasePath, name));
385a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            out.close();
386a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        }
387a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
388a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
389a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private void write(String name, String value) throws IOException {
390a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final DataOutputStream out = new DataOutputStream(
391a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                new FileOutputStream(new File(mBasePath, name)));
392a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        out.writeUTF(value);
393a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        out.close();
394a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
395a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
396a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static Writer writer(final String value) {
397a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        return new Writer() {
398a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            public void write(OutputStream out) throws IOException {
399a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                new DataOutputStream(out).writeUTF(value);
400a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            }
401a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        };
402a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
403a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
404a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static void assertReadAll(FileRotator rotate, String... expected) throws IOException {
405a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        assertReadMatching(rotate, Long.MIN_VALUE, Long.MAX_VALUE, expected);
406a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
407a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
408a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static void assertReadMatching(
409a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            FileRotator rotate, long matchStartMillis, long matchEndMillis, String... expected)
410a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            throws IOException {
411a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        final RecordingReader reader = new RecordingReader();
412a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        rotate.readMatching(reader, matchStartMillis, matchEndMillis);
413a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        reader.assertRead(expected);
414a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
415a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
416a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    private static class RecordingReader implements Reader {
417a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        private ArrayList<String> mActual = Lists.newArrayList();
418a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
419a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        public void read(InputStream in) throws IOException {
420a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            mActual.add(new DataInputStream(in).readUTF());
421a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        }
422a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
423a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        public void reset() {
424a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            mActual.clear();
425a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        }
426a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
427a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        public void assertRead(String... expected) {
428a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            assertEquals(expected.length, mActual.size());
429a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey
430a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            final ArrayList<String> actualCopy = new ArrayList<String>(mActual);
431a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            for (String value : expected) {
432a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                if (!actualCopy.remove(value)) {
433a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                    final String expectedString = Arrays.toString(expected);
434a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                    final String actualString = Arrays.toString(mActual.toArray());
435a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                    fail("expected: " + expectedString + " but was: " + actualString);
436a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey                }
437a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey            }
438a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey        }
439a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey    }
440a27a3e8ad7d20dea63ef2d5cb8b6ec7e56c20a89Jeff Sharkey}
441