1/*
2 * Copyright 2016 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#define LOG_TAG "MtpFfsHandle_test.cpp"
17
18#include <android-base/unique_fd.h>
19#include <android-base/test_utils.h>
20#include <fcntl.h>
21#include <gtest/gtest.h>
22#include <memory>
23#include <random>
24#include <string>
25#include <unistd.h>
26#include <utils/Log.h>
27
28#include "MtpFfsHandle.h"
29
30namespace android {
31
32constexpr int MAX_FILE_CHUNK_SIZE = 3 * 1024 * 1024;
33
34constexpr int TEST_PACKET_SIZE = 512;
35constexpr int SMALL_MULT = 30;
36constexpr int MED_MULT = 510;
37
38static const std::string dummyDataStr =
39    "/*\n * Copyright 2015 The Android Open Source Project\n *\n * Licensed un"
40    "der the Apache License, Version 2.0 (the \"License\");\n * you may not us"
41    "e this file except in compliance with the License.\n * You may obtain a c"
42    "opy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE"
43    "-2.0\n *\n * Unless required by applicable law or agreed to in writing, s"
44    "oftware\n * distributed under the License is distributed on an \"AS IS\" "
45    "BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express o"
46    "r implied.\n * Se";
47
48/**
49 * Functional tests for the MtpFfsHandle class. Ensures header and data integrity
50 * by mocking ffs endpoints as pipes to capture input / output.
51 */
52class MtpFfsHandleTest : public ::testing::Test {
53protected:
54    std::unique_ptr<IMtpHandle> handle;
55
56    // Pipes for reading endpoint data
57    android::base::unique_fd bulk_in;
58    android::base::unique_fd bulk_out;
59    android::base::unique_fd intr;
60
61    TemporaryFile dummy_file;
62
63    MtpFfsHandleTest() {
64        int fd[2];
65        handle = std::unique_ptr<IMtpHandle>(get_ffs_handle());
66        MtpFfsHandle *ffs_handle = static_cast<MtpFfsHandle*>(handle.get());
67        EXPECT_TRUE(ffs_handle != NULL);
68
69        EXPECT_EQ(pipe(fd), 0);
70        EXPECT_EQ(fcntl(fd[0], F_SETPIPE_SZ, 1048576), 1048576);
71        bulk_in.reset(fd[0]);
72        ffs_handle->mBulkIn.reset(fd[1]);
73
74        EXPECT_EQ(pipe(fd), 0);
75        EXPECT_EQ(fcntl(fd[0], F_SETPIPE_SZ, 1048576), 1048576);
76        bulk_out.reset(fd[1]);
77        ffs_handle->mBulkOut.reset(fd[0]);
78
79        EXPECT_EQ(pipe(fd), 0);
80        intr.reset(fd[0]);
81        ffs_handle->mIntr.reset(fd[1]);
82
83        ffs_handle->mBuffer1.resize(MAX_FILE_CHUNK_SIZE);
84        ffs_handle->mBuffer2.resize(MAX_FILE_CHUNK_SIZE);
85    }
86
87    ~MtpFfsHandleTest() {}
88};
89
90TEST_F(MtpFfsHandleTest, testRead) {
91    EXPECT_EQ(write(bulk_out, dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
92    char buf[TEST_PACKET_SIZE + 1];
93    buf[TEST_PACKET_SIZE] = '\0';
94    EXPECT_EQ(handle->read(buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
95    EXPECT_STREQ(buf, dummyDataStr.c_str());
96}
97
98TEST_F(MtpFfsHandleTest, testWrite) {
99    char buf[TEST_PACKET_SIZE + 1];
100    buf[TEST_PACKET_SIZE] = '\0';
101    EXPECT_EQ(handle->write(dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
102    EXPECT_EQ(read(bulk_in, buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
103    EXPECT_STREQ(buf, dummyDataStr.c_str());
104}
105
106TEST_F(MtpFfsHandleTest, testReceiveFileSmall) {
107    std::stringstream ss;
108    mtp_file_range mfr;
109    int size = TEST_PACKET_SIZE * SMALL_MULT;
110    char buf[size + 1];
111    buf[size] = '\0';
112
113    mfr.length = size;
114    mfr.fd = dummy_file.fd;
115    for (int i = 0; i < SMALL_MULT; i++)
116        ss << dummyDataStr;
117
118    EXPECT_EQ(write(bulk_out, ss.str().c_str(), size), size);
119    EXPECT_EQ(handle->receiveFile(mfr, false), 0);
120
121    EXPECT_EQ(read(dummy_file.fd, buf, size), size);
122
123    EXPECT_STREQ(buf, ss.str().c_str());
124}
125
126TEST_F(MtpFfsHandleTest, testReceiveFileMed) {
127    std::stringstream ss;
128    mtp_file_range mfr;
129    int size = TEST_PACKET_SIZE * MED_MULT;
130    char buf[size + 1];
131    buf[size] = '\0';
132
133    mfr.length = size;
134    mfr.fd = dummy_file.fd;
135    for (int i = 0; i < MED_MULT; i++)
136        ss << dummyDataStr;
137
138    EXPECT_EQ(write(bulk_out, ss.str().c_str(), size), size);
139    EXPECT_EQ(handle->receiveFile(mfr, false), 0);
140
141    EXPECT_EQ(read(dummy_file.fd, buf, size), size);
142
143    EXPECT_STREQ(buf, ss.str().c_str());
144}
145
146TEST_F(MtpFfsHandleTest, testSendFileSmall) {
147    std::stringstream ss;
148    mtp_file_range mfr;
149    mfr.command = 42;
150    mfr.transaction_id = 1337;
151    mfr.offset = 0;
152    int size = TEST_PACKET_SIZE * SMALL_MULT;
153    char buf[size + sizeof(mtp_data_header) + 1];
154    buf[size + sizeof(mtp_data_header)] = '\0';
155
156    mfr.length = size;
157    mfr.fd = dummy_file.fd;
158    for (int i = 0; i < SMALL_MULT; i++)
159        ss << dummyDataStr;
160
161    EXPECT_EQ(write(dummy_file.fd, ss.str().c_str(), size), size);
162    EXPECT_EQ(handle->sendFile(mfr), 0);
163
164    EXPECT_EQ(read(bulk_in, buf, size + sizeof(mtp_data_header)),
165            static_cast<long>(size + sizeof(mtp_data_header)));
166
167    struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(buf);
168    EXPECT_STREQ(buf + sizeof(mtp_data_header), ss.str().c_str());
169    EXPECT_EQ(header->length, static_cast<unsigned int>(size + sizeof(mtp_data_header)));
170    EXPECT_EQ(header->type, static_cast<unsigned int>(2));
171    EXPECT_EQ(header->command, static_cast<unsigned int>(42));
172    EXPECT_EQ(header->transaction_id, static_cast<unsigned int>(1337));
173}
174
175TEST_F(MtpFfsHandleTest, testSendFileMed) {
176    std::stringstream ss;
177    mtp_file_range mfr;
178    mfr.command = 42;
179    mfr.transaction_id = 1337;
180    mfr.offset = 0;
181    int size = TEST_PACKET_SIZE * MED_MULT;
182    char buf[size + sizeof(mtp_data_header) + 1];
183    buf[size + sizeof(mtp_data_header)] = '\0';
184
185    mfr.length = size;
186    mfr.fd = dummy_file.fd;
187    for (int i = 0; i < MED_MULT; i++)
188        ss << dummyDataStr;
189
190    EXPECT_EQ(write(dummy_file.fd, ss.str().c_str(), size), size);
191    EXPECT_EQ(handle->sendFile(mfr), 0);
192
193    EXPECT_EQ(read(bulk_in, buf, size + sizeof(mtp_data_header)),
194            static_cast<long>(size + sizeof(mtp_data_header)));
195
196    struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(buf);
197    EXPECT_STREQ(buf + sizeof(mtp_data_header), ss.str().c_str());
198    EXPECT_EQ(header->length, static_cast<unsigned int>(size + sizeof(mtp_data_header)));
199    EXPECT_EQ(header->type, static_cast<unsigned int>(2));
200    EXPECT_EQ(header->command, static_cast<unsigned int>(42));
201    EXPECT_EQ(header->transaction_id, static_cast<unsigned int>(1337));
202}
203
204TEST_F(MtpFfsHandleTest, testSendFileMedPartial) {
205    std::stringstream ss;
206    mtp_file_range mfr;
207    mfr.fd = dummy_file.fd;
208    mfr.command = 42;
209    mfr.transaction_id = 1337;
210    int size = TEST_PACKET_SIZE * MED_MULT;
211    char buf[size + 1];
212    buf[size] = '\0';
213
214    for (int i = 0; i < MED_MULT; i++)
215        ss << dummyDataStr;
216
217    EXPECT_EQ(write(dummy_file.fd, ss.str().c_str(), size), size);
218
219    std::random_device rd;
220    std::mt19937 gen(rd());
221    std::uniform_int_distribution<> dis(1, TEST_PACKET_SIZE);
222    int offset = 0;
223    while (offset != size) {
224        mfr.offset = offset;
225        int length = std::min(size - offset, dis(gen));
226        mfr.length = length;
227        char temp_buf[length + sizeof(mtp_data_header)];
228        EXPECT_EQ(handle->sendFile(mfr), 0);
229
230        EXPECT_EQ(read(bulk_in, temp_buf, length + sizeof(mtp_data_header)),
231                static_cast<long>(length + sizeof(mtp_data_header)));
232
233        struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(temp_buf);
234        EXPECT_EQ(header->length, static_cast<unsigned int>(length + sizeof(mtp_data_header)));
235        EXPECT_EQ(header->type, static_cast<unsigned int>(2));
236        EXPECT_EQ(header->command, static_cast<unsigned int>(42));
237        EXPECT_EQ(header->transaction_id, static_cast<unsigned int>(1337));
238        memcpy(buf + offset, temp_buf + sizeof(mtp_data_header), length);
239        offset += length;
240    }
241    EXPECT_STREQ(buf, ss.str().c_str());
242}
243
244TEST_F(MtpFfsHandleTest, testSendFileEmpty) {
245    mtp_file_range mfr;
246    mfr.command = 42;
247    mfr.transaction_id = 1337;
248    mfr.offset = 0;
249    int size = 0;
250    char buf[size + sizeof(mtp_data_header) + 1];
251    buf[size + sizeof(mtp_data_header)] = '\0';
252
253    mfr.length = size;
254    mfr.fd = dummy_file.fd;
255
256    EXPECT_EQ(handle->sendFile(mfr), 0);
257
258    EXPECT_EQ(read(bulk_in, buf, size + sizeof(mtp_data_header)),
259            static_cast<long>(size + sizeof(mtp_data_header)));
260
261    struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(buf);
262    EXPECT_EQ(header->length, static_cast<unsigned int>(size + sizeof(mtp_data_header)));
263    EXPECT_EQ(header->type, static_cast<unsigned int>(2));
264    EXPECT_EQ(header->command, static_cast<unsigned int>(42));
265    EXPECT_EQ(header->transaction_id, static_cast<unsigned int>(1337));
266}
267
268TEST_F(MtpFfsHandleTest, testSendEvent) {
269    struct mtp_event event;
270    event.length = TEST_PACKET_SIZE;
271    event.data = const_cast<char*>(dummyDataStr.c_str());
272    char buf[TEST_PACKET_SIZE + 1];
273    buf[TEST_PACKET_SIZE] = '\0';
274
275    handle->sendEvent(event);
276    read(intr, buf, TEST_PACKET_SIZE);
277    EXPECT_STREQ(buf, dummyDataStr.c_str());
278}
279
280} // namespace android
281