1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.backup.utils;
18
19import static com.google.common.truth.Truth.assertThat;
20
21import static org.junit.Assert.fail;
22import static org.mockito.Mockito.never;
23import static org.mockito.Mockito.verify;
24import static org.mockito.Mockito.verifyZeroInteractions;
25
26import android.os.ParcelFileDescriptor;
27import android.platform.test.annotations.Presubmit;
28import android.support.test.filters.SmallTest;
29import android.support.test.runner.AndroidJUnit4;
30
31import org.junit.After;
32import org.junit.Before;
33import org.junit.Test;
34import org.junit.runner.RunWith;
35import org.mockito.Mock;
36import org.mockito.MockitoAnnotations;
37
38import java.io.ByteArrayOutputStream;
39import java.io.DataOutputStream;
40import java.io.EOFException;
41import java.io.File;
42import java.io.FileOutputStream;
43import java.io.IOException;
44import java.io.OutputStream;
45import java.util.Random;
46
47@SmallTest
48@Presubmit
49@RunWith(AndroidJUnit4.class)
50public class FullBackupUtilsTest {
51    @Mock private ParcelFileDescriptor mParcelFileDescriptorMock;
52    @Mock private OutputStream mOutputStreamMock;
53    private File mTemporaryFile;
54    private ByteArrayOutputStream mByteArrayOutputStream;
55    private ParcelFileDescriptor mTemporaryFileDescriptor;
56
57    @Before
58    public void setUp() throws Exception {
59        MockitoAnnotations.initMocks(this);
60
61        mTemporaryFile = File.createTempFile("backup-data", ".txt");
62        mByteArrayOutputStream = new ByteArrayOutputStream();
63    }
64
65    @After
66    public void tearDown() throws Exception {
67        if (mTemporaryFileDescriptor != null) {
68            mTemporaryFileDescriptor.close();
69        }
70        if (mTemporaryFile != null) {
71            mTemporaryFile.delete();
72        }
73    }
74
75    @Test
76    public void routeSocketDataToOutput_inPipeIsNull_throwsNPE() throws Exception {
77        try {
78            FullBackupUtils.routeSocketDataToOutput(null, mOutputStreamMock);
79            fail();
80        } catch (NullPointerException expected) {
81        }
82    }
83
84    @Test
85    public void routeSocketDataToOutput_outNull_throwsNPE() throws Exception {
86        try {
87            FullBackupUtils.routeSocketDataToOutput(mParcelFileDescriptorMock, null);
88            fail();
89        } catch (NullPointerException expected) {
90        }
91    }
92
93    @Test
94    public void routeSocketDataToOutput_emptyInput_throwsEOFException() throws Exception {
95        DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(mTemporaryFile));
96        outputStream.close();
97
98        mTemporaryFileDescriptor = ParcelFileDescriptor.open(mTemporaryFile,
99                ParcelFileDescriptor.MODE_READ_ONLY);
100
101        try {
102            FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor,
103                    mOutputStreamMock);
104            fail();
105        } catch (EOFException expected) {
106        }
107
108        verifyZeroInteractions(mOutputStreamMock);
109        assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
110    }
111
112    @Test
113    public void routeSocketDataToOutput_incompleteChunkSizeInput_throwsEOFException()
114            throws Exception {
115        DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(mTemporaryFile));
116        outputStream.writeByte(100);
117        outputStream.close();
118
119        mTemporaryFileDescriptor = ParcelFileDescriptor.open(mTemporaryFile,
120                ParcelFileDescriptor.MODE_READ_ONLY);
121
122        try {
123            FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor,
124                    mOutputStreamMock);
125            fail();
126        } catch (EOFException expected) {
127        }
128
129        verifyZeroInteractions(mOutputStreamMock);
130        assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
131    }
132
133    @Test
134    public void routeSocketDataToOutput_validEmptyInput_doesNotWriteAnything() throws Exception {
135        DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(mTemporaryFile));
136        outputStream.writeInt(0);
137        outputStream.close();
138
139        mTemporaryFileDescriptor = ParcelFileDescriptor.open(mTemporaryFile,
140                ParcelFileDescriptor.MODE_READ_ONLY);
141
142        FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor, mOutputStreamMock);
143
144        verifyZeroInteractions(mOutputStreamMock);
145        assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
146    }
147
148    @Test
149    public void routeSocketDataToOutput_notEnoughData_throwsEOFException() throws Exception {
150        byte[] data = createFakeDataArray(100);
151        DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(mTemporaryFile));
152        outputStream.writeInt(data.length + 1);
153        outputStream.write(data);
154        outputStream.close();
155
156        mTemporaryFileDescriptor = ParcelFileDescriptor.open(mTemporaryFile,
157                ParcelFileDescriptor.MODE_READ_ONLY);
158
159        try {
160            FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor,
161                    mByteArrayOutputStream);
162            fail();
163        } catch (EOFException expected) {
164        }
165
166        verify(mOutputStreamMock, never()).close();
167        assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
168    }
169
170    @Test
171    public void routeSocketDataToOutput_oneSmallChunk_writesOutputCorrectly() throws Exception {
172        byte[] data = createFakeDataArray(100);
173        DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(mTemporaryFile));
174        outputStream.writeInt(data.length);
175        outputStream.write(data);
176        outputStream.writeInt(0);
177        outputStream.close();
178
179        mTemporaryFileDescriptor = ParcelFileDescriptor.open(mTemporaryFile,
180                ParcelFileDescriptor.MODE_READ_ONLY);
181
182        FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor,
183                mByteArrayOutputStream);
184
185        assertThat(mByteArrayOutputStream.toByteArray()).isEqualTo(data);
186        verify(mOutputStreamMock, never()).close();
187        assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
188    }
189
190    @Test
191    public void routeSocketDataToOutput_oneLargeChunk_writesOutputCorrectly() throws Exception {
192        byte[] data = createFakeDataArray(128000);
193        DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(mTemporaryFile));
194        outputStream.writeInt(data.length);
195        outputStream.write(data);
196        outputStream.writeInt(0);
197        outputStream.close();
198
199        mTemporaryFileDescriptor = ParcelFileDescriptor.open(mTemporaryFile,
200                ParcelFileDescriptor.MODE_READ_ONLY);
201
202        FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor,
203                mByteArrayOutputStream);
204
205        assertThat(mByteArrayOutputStream.toByteArray()).isEqualTo(data);
206        verify(mOutputStreamMock, never()).close();
207        assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
208    }
209
210    @Test
211    public void routeSocketDataToOutput_twoSmallChunks_writesOutputCorrectly() throws Exception {
212        byte[] data = createFakeDataArray(200);
213        int chunk1Length = 97;
214        int chunk2Length = data.length - chunk1Length;
215
216        DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(mTemporaryFile));
217        outputStream.writeInt(chunk1Length);
218        outputStream.write(data, 0, chunk1Length);
219        outputStream.writeInt(chunk2Length);
220        outputStream.write(data, chunk1Length, chunk2Length);
221        outputStream.writeInt(0);
222        outputStream.close();
223
224        mTemporaryFileDescriptor = ParcelFileDescriptor.open(mTemporaryFile,
225                ParcelFileDescriptor.MODE_READ_ONLY);
226
227        FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor,
228                mByteArrayOutputStream);
229
230        assertThat(mByteArrayOutputStream.toByteArray()).isEqualTo(data);
231        verify(mOutputStreamMock, never()).close();
232        assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
233    }
234
235    @Test
236    public void routeSocketDataToOutput_twoLargeChunks_writesOutputCorrectly() throws Exception {
237        byte[] data = createFakeDataArray(256000);
238        int chunk1Length = 127313;
239        int chunk2Length = data.length - chunk1Length;
240
241        DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(mTemporaryFile));
242        outputStream.writeInt(chunk1Length);
243        outputStream.write(data, 0, chunk1Length);
244        outputStream.writeInt(chunk2Length);
245        outputStream.write(data, chunk1Length, chunk2Length);
246        outputStream.writeInt(0);
247        outputStream.close();
248
249        mTemporaryFileDescriptor = ParcelFileDescriptor.open(mTemporaryFile,
250                ParcelFileDescriptor.MODE_READ_ONLY);
251
252        FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor,
253                mByteArrayOutputStream);
254
255        assertThat(mByteArrayOutputStream.toByteArray()).isEqualTo(data);
256        verify(mOutputStreamMock, never()).close();
257        assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
258    }
259
260    private static byte[] createFakeDataArray(int length) {
261        byte[] data = new byte[length];
262        new Random(3742).nextBytes(data);
263        return data;
264    }
265}
266