Merge lp:~charlesk/keeper/keeper-tar-create-handle-bad-user-inputs-gracefully into lp:keeper

Proposed by Charles Kerr
Status: Merged
Merge reported by: Charles Kerr
Merged at revision: not available
Proposed branch: lp:~charlesk/keeper/keeper-tar-create-handle-bad-user-inputs-gracefully
Merge into: lp:keeper
Prerequisite: lp:~charlesk/keeper/service-dbusmock-should-notice-helper-failure
Diff against target: 417 lines (+221/-60)
9 files modified
src/tar/main.cpp (+16/-0)
src/tar/tar-creator.cpp (+7/-2)
tests/CMakeLists.txt (+0/-2)
tests/unit/CMakeLists.txt (+0/-5)
tests/unit/tar/CMakeLists.txt (+36/-0)
tests/unit/tar/keeper-tar-create-test.cpp (+87/-5)
tests/unit/tar/ktc-invoke-nobus.sh.in (+2/-0)
tests/unit/tar/ktc-invoke-nofiles.sh.in (+1/-0)
tests/unit/tar/tar-creator-test.cpp (+72/-46)
To merge this branch: bzr merge lp:~charlesk/keeper/keeper-tar-create-handle-bad-user-inputs-gracefully
Reviewer Review Type Date Requested Status
Xavi Garcia (community) Approve
Review via email: mp+301724@code.launchpad.net

Commit message

keeper-tar-create should fail gracefully if the user invokes without a busname or filenames.

Description of the change

keeper-tar-create should fail gracefully if the user invokes without a busname or filenames.

Add unit tests to confirm an error is returned in either of those two situations.

To post a comment you must log in.
61. By Charles Kerr

sync with trunk

Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

Looks like the branch should depend lp:~charlesk/keeper/service-dbusmock-should-notice-helper-failure....

Looks good to me, only one question about how it waits for stdin...

review: Needs Information
62. By Charles Kerr

use QIODevice.waitForReadyready() instead of calling select() manually

63. By Charles Kerr

sync with trunk

64. By Charles Kerr

revert r62, we're not in a QApplication so waiting on Qt's event loop isn't as quick and easy as it seems

Revision history for this message
Charles Kerr (charlesk) :
Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/tar/main.cpp'
2--- src/tar/main.cpp 2016-07-13 02:20:24 +0000
3+++ src/tar/main.cpp 2016-08-03 16:39:30 +0000
4@@ -32,8 +32,10 @@
5
6 #include <glib.h>
7
8+#include <sys/select.h>
9 #include <unistd.h>
10
11+#include <cstdio> // fileno()
12 #include <ctime>
13 #include <iostream>
14 #include <type_traits>
15@@ -44,6 +46,20 @@
16 QStringList
17 get_filenames_from_file(FILE * fp, bool zero)
18 {
19+ // don't wait forever...
20+ int fd = fileno(fp);
21+ fd_set readfds;
22+ FD_ZERO(&readfds);
23+ FD_SET(fd, &readfds);
24+ struct timeval tv {};
25+ tv.tv_sec = 2;
26+ select(1, &readfds, NULL, NULL, &tv);
27+ if (!FD_ISSET(fd, &readfds)) {
28+ qWarning() << "Couldn't read files from stdin";
29+ return QStringList();
30+ }
31+
32+ // read the file list
33 QFile file;
34 file.open(fp, QIODevice::ReadOnly);
35 auto filenames_raw = file.readAll();
36
37=== modified file 'src/tar/tar-creator.cpp'
38--- src/tar/tar-creator.cpp 2016-07-22 23:36:45 +0000
39+++ src/tar/tar-creator.cpp 2016-08-03 16:39:30 +0000
40@@ -179,8 +179,13 @@
41 ret = archive_write_header(archive, entry);
42 if (ret==ARCHIVE_WARN)
43 qWarning() << archive_error_string(archive);
44- if (ret==ARCHIVE_FATAL)
45- qFatal("%s", archive_error_string(archive));
46+ if (ret==ARCHIVE_FATAL) {
47+ auto errstr = QString::fromUtf8("Error adding header for '%1': %2")
48+ .arg(filename)
49+ .arg(archive_error_string(archive));
50+ qWarning() << qPrintable(errstr);
51+ throw std::runtime_error(errstr.toStdString());
52+ }
53 } while (ret == ARCHIVE_RETRY);
54
55 archive_entry_free(entry);
56
57=== modified file 'tests/CMakeLists.txt'
58--- tests/CMakeLists.txt 2016-08-02 09:14:49 +0000
59+++ tests/CMakeLists.txt 2016-08-03 16:39:30 +0000
60@@ -20,7 +20,6 @@
61 )
62
63 set(KEEPER_TAR_CREATE_BIN ${CMAKE_BINARY_DIR}/src/tar/keeper-tar-create)
64-set(KEEPER_TAR_CREATE_INVOKE ${CMAKE_CURRENT_BINARY_DIR}/tar/keeper-tar-create-invoke.sh)
65 set(KEPPER_HELPER_TEST_LOCATION ${CMAKE_BINARY_DIR}/tests/fakes/helpers-test.sh)
66
67 add_definitions(
68@@ -31,7 +30,6 @@
69 -DKEEPER_CLIENT_BIN="${CMAKE_BINARY_DIR}/src/cli/keeper"
70 -DTEST_SIMPLE_HELPER_SH="${KEPPER_HELPER_TEST_LOCATION}"
71 -DKEEPER_TAR_CREATE_BIN="${KEEPER_TAR_CREATE_BIN}"
72- -DKEEPER_TAR_CREATE_INVOKE="${KEEPER_TAR_CREATE_INVOKE}"
73 )
74
75 set(SERVICE_TEMPLATE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/com_canonical_keeper.py")
76
77=== modified file 'tests/unit/CMakeLists.txt'
78--- tests/unit/CMakeLists.txt 2016-07-31 17:20:13 +0000
79+++ tests/unit/CMakeLists.txt 2016-08-03 16:39:30 +0000
80@@ -8,11 +8,6 @@
81 ${HELPERS_TEST_DEPS_INCLUDE_DIRS}
82 )
83
84-configure_file(
85- ${CMAKE_CURRENT_SOURCE_DIR}/tar/keeper-tar-create-invoke.sh.in
86- ${KEEPER_TAR_CREATE_INVOKE}
87-)
88-
89 set(
90 UNIT_TEST_LIBRARIES
91 backup-helper
92
93=== modified file 'tests/unit/tar/CMakeLists.txt'
94--- tests/unit/tar/CMakeLists.txt 2016-07-31 17:20:13 +0000
95+++ tests/unit/tar/CMakeLists.txt 2016-08-03 16:39:30 +0000
96@@ -1,4 +1,40 @@
97 #
98+# create the wrapper scripts
99+#
100+
101+set(
102+ KTC_INVOKE
103+ ${CMAKE_CURRENT_BINARY_DIR}/ktc-invoke.sh
104+)
105+configure_file(
106+ ${CMAKE_CURRENT_SOURCE_DIR}/ktc-invoke.sh.in
107+ ${KTC_INVOKE}
108+)
109+set(
110+ KTC_INVOKE_NOBUS
111+ ${CMAKE_CURRENT_BINARY_DIR}/ktc-invoke-nobus.sh
112+)
113+configure_file(
114+ ${CMAKE_CURRENT_SOURCE_DIR}/ktc-invoke-nobus.sh.in
115+ ${KTC_INVOKE_NOBUS}
116+)
117+set(
118+ KTC_INVOKE_NOFILES
119+ ${CMAKE_CURRENT_BINARY_DIR}/ktc-invoke-nofiles.sh
120+)
121+configure_file(
122+ ${CMAKE_CURRENT_SOURCE_DIR}/ktc-invoke-nofiles.sh.in
123+ ${KTC_INVOKE_NOFILES}
124+)
125+
126+add_definitions(
127+ -DKTC_INVOKE="${KTC_INVOKE}"
128+ -DKTC_INVOKE_NOBUS="${KTC_INVOKE_NOBUS}"
129+ -DKTC_INVOKE_NOFILES="${KTC_INVOKE_NOFILES}"
130+)
131+
132+
133+#
134 # tar-creator-test
135 #
136
137
138=== modified file 'tests/unit/tar/keeper-tar-create-test.cpp'
139--- tests/unit/tar/keeper-tar-create-test.cpp 2016-08-01 19:34:24 +0000
140+++ tests/unit/tar/keeper-tar-create-test.cpp 2016-08-03 16:39:30 +0000
141@@ -26,14 +26,34 @@
142 #include <QTemporaryDir>
143
144
145-using KeeperTarCreateFixture = KeeperDBusMockFixture;
146+/***
147+****
148+***/
149+
150+class KeeperTarCreateFixture: public KeeperDBusMockFixture
151+{
152+ using parent = KeeperDBusMockFixture;
153+
154+ void SetUp() override
155+ {
156+ parent::SetUp();
157+
158+ qsrand(time(nullptr));
159+ }
160+
161+ void TearDown() override
162+ {
163+ }
164+};
165+
166+/***
167+****
168+***/
169
170 TEST_F(KeeperTarCreateFixture, BackupRun)
171 {
172 static constexpr int n_runs {10};
173
174- qsrand(time(nullptr));
175-
176 for (int i=0; i<n_runs; ++i)
177 {
178 // build a directory full of random files
179@@ -45,13 +65,13 @@
180 { KEY_NAME, QDir(in.path()).dirName() },
181 { KEY_TYPE, QStringLiteral("folder") },
182 { KEY_SUBTYPE, in.path() },
183- { KEY_HELPER, QString::fromUtf8(KEEPER_TAR_CREATE_INVOKE) }
184+ { KEY_HELPER, QString::fromUtf8(KTC_INVOKE) }
185 });
186
187 // start the backup
188 QDBusReply<void> reply = user_iface_->call("StartBackup", QStringList{uuid});
189 ASSERT_TRUE(reply.isValid()) << qPrintable(reply.error().message());
190- wait_for_tasks_to_finish();
191+ ASSERT_TRUE(wait_for_tasks_to_finish());
192
193 // ask keeper for the blob
194 QDBusReply<QByteArray> blob = mock_iface_->call(QStringLiteral("GetBackupData"), uuid);
195@@ -75,3 +95,65 @@
196 EXPECT_TRUE(FileUtils::compareDirectories(in.path(), out.path()));
197 }
198 }
199+
200+/***
201+****
202+***/
203+
204+TEST_F(KeeperTarCreateFixture, BadArgNoBus)
205+{
206+ // build a directory full of random files
207+ QTemporaryDir in;
208+ FileUtils::fillTemporaryDirectory(in.path());
209+
210+ // tell keeper that's a backup choice
211+ const auto uuid = add_backup_choice(QMap<QString,QVariant>{
212+ { KEY_NAME, QDir(in.path()).dirName() },
213+ { KEY_TYPE, QStringLiteral("folder") },
214+ { KEY_SUBTYPE, in.path() },
215+ { KEY_HELPER, QString::fromUtf8(KTC_INVOKE_NOBUS) }
216+ });
217+
218+ // start the backup
219+ QDBusReply<void> reply = user_iface_->call("StartBackup", QStringList{uuid});
220+ ASSERT_TRUE(reply.isValid()) << qPrintable(reply.error().message());
221+ EXPECT_TRUE(wait_for_tasks_to_finish());
222+
223+ // confirm that the backup ended in error
224+ const auto state = user_iface_->state();
225+ const auto& properties = state[uuid];
226+ EXPECT_EQ(QString::fromUtf8("failed"), properties.value(KEY_ACTION))
227+ << qPrintable(properties.value(KEY_ACTION).toString());
228+ EXPECT_FALSE(properties.value(KEY_ERROR).toString().isEmpty());
229+}
230+
231+/***
232+****
233+***/
234+
235+TEST_F(KeeperTarCreateFixture, BadArgNoFiles)
236+{
237+ // build a directory full of random files
238+ QTemporaryDir in;
239+ FileUtils::fillTemporaryDirectory(in.path());
240+
241+ // tell keeper that's a backup choice
242+ const auto uuid = add_backup_choice(QMap<QString,QVariant>{
243+ { KEY_NAME, QDir(in.path()).dirName() },
244+ { KEY_TYPE, QStringLiteral("folder") },
245+ { KEY_SUBTYPE, in.path() },
246+ { KEY_HELPER, QString::fromUtf8(KTC_INVOKE_NOFILES) }
247+ });
248+
249+ // start the backup
250+ QDBusReply<void> reply = user_iface_->call("StartBackup", QStringList{uuid});
251+ ASSERT_TRUE(reply.isValid()) << qPrintable(reply.error().message());
252+ EXPECT_TRUE(wait_for_tasks_to_finish());
253+
254+ // confirm that the backup ended in error
255+ const auto state = user_iface_->state();
256+ const auto& properties = state[uuid];
257+ EXPECT_EQ(QString::fromUtf8("failed"), properties.value(KEY_ACTION))
258+ << qPrintable(properties.value(KEY_ACTION).toString());
259+ EXPECT_FALSE(properties.value(KEY_ERROR).toString().isEmpty());
260+}
261
262=== added file 'tests/unit/tar/ktc-invoke-nobus.sh.in'
263--- tests/unit/tar/ktc-invoke-nobus.sh.in 1970-01-01 00:00:00 +0000
264+++ tests/unit/tar/ktc-invoke-nobus.sh.in 2016-08-03 16:39:30 +0000
265@@ -0,0 +1,2 @@
266+find ./ -type f
267+find ./ -type f -print0 | @KEEPER_TAR_CREATE_BIN@ -0
268
269=== added file 'tests/unit/tar/ktc-invoke-nofiles.sh.in'
270--- tests/unit/tar/ktc-invoke-nofiles.sh.in 1970-01-01 00:00:00 +0000
271+++ tests/unit/tar/ktc-invoke-nofiles.sh.in 2016-08-03 16:39:30 +0000
272@@ -0,0 +1,1 @@
273+@KEEPER_TAR_CREATE_BIN@ -0 -a /com/canonical/keeper/helper
274
275=== renamed file 'tests/unit/tar/keeper-tar-create-invoke.sh.in' => 'tests/unit/tar/ktc-invoke.sh.in'
276=== modified file 'tests/unit/tar/tar-creator-test.cpp'
277--- tests/unit/tar/tar-creator-test.cpp 2016-07-22 13:25:07 +0000
278+++ tests/unit/tar/tar-creator-test.cpp 2016-08-03 16:39:30 +0000
279@@ -24,66 +24,92 @@
280 #include <gtest/gtest.h>
281
282 #include <QDebug>
283+#include <QDir>
284+#include <QFile>
285+#include <QFileInfo>
286+#include <QProcess>
287 #include <QString>
288 #include <QTemporaryDir>
289
290 #include <algorithm>
291+#include <array>
292 #include <cstdio>
293
294 class TarCreatorFixture: public ::testing::Test
295 {
296 protected:
297
298+ void SetUp() override
299+ {
300+ qsrand(time(nullptr));
301+ }
302+
303+ void TearDown() override
304+ {
305+ }
306+
307 };
308
309-
310-TEST_F(TarCreatorFixture, HelloWorld)
311-{
312-}
313-
314-TEST_F(TarCreatorFixture, CreateUncompressed)
315-{
316- static constexpr int n_runs = 10;
317-
318- qsrand(time(nullptr));
319+/***
320+****
321+***/
322+
323+TEST_F(TarCreatorFixture, Create)
324+{
325+ static constexpr int n_runs {5};
326
327 for (int i=0; i<n_runs; ++i)
328 {
329- // build a directory full of random files
330- QTemporaryDir sandbox;
331- ASSERT_TRUE(FileUtils::fillTemporaryDirectory(sandbox.path()));
332- const auto files = FileUtils::getFilesRecursively(sandbox.path());
333-
334- // create the tar creator
335- TarCreator tar_creator(files, false);
336-
337- // simple sanity check on its size estimate
338- const auto filesize_sum = std::accumulate(files.begin(), files.end(), 0, [](ssize_t sum, QString const& filename){return sum + QFileInfo(filename).size();});
339-
340-#if 0
341-ssize_t filesize_sum {};
342-template< class InputIt, class T, class BinaryOperation >
343-T accumulate( InputIt first, InputIt last, T init,
344- BinaryOperation op );
345- for (const auto& file : files)
346- filesize_sum += QFileInfo(file).size();
347-#endif
348- const auto estimated_size = tar_creator.calculate_size();
349- ASSERT_GT(estimated_size, filesize_sum);
350-
351- // does it match the actual size?
352- size_t actual_size {};
353- std::vector<char> buf;
354- while (tar_creator.step(buf))
355- actual_size += buf.size();
356- ASSERT_EQ(estimated_size, actual_size);
357-
358- // FIXME: now extract and confirm the checksums are the same
359+ for (const auto compression_enabled : std::array<bool,2>{false, true})
360+ {
361+ // build a directory full of random files
362+ QTemporaryDir in;
363+ QDir indir(in.path());
364+ ASSERT_TRUE(FileUtils::fillTemporaryDirectory(in.path()));
365+
366+ // create the tar creator
367+ EXPECT_TRUE(QDir::setCurrent(in.path()));
368+ QStringList files;
369+ for (file : FileUtils::getFilesRecursively(in.path()))
370+ files += indir.relativeFilePath(file);
371+ TarCreator tar_creator(files, compression_enabled);
372+
373+ // simple sanity check on its size estimate
374+ const auto estimated_size = tar_creator.calculate_size();
375+ const auto filesize_sum = std::accumulate(
376+ files.begin(),
377+ files.end(),
378+ 0,
379+ [](ssize_t sum, QString const& filename){return sum + QFileInfo(filename).size();}
380+ );
381+ if (!compression_enabled)
382+ ASSERT_GT(estimated_size, filesize_sum);
383+
384+ // does it match the actual size?
385+ size_t actual_size {};
386+ std::vector<char> contents, step;
387+ while (tar_creator.step(step)) {
388+ contents.insert(contents.end(), step.begin(), step.end());
389+ actual_size += step.size();
390+ }
391+ ASSERT_EQ(estimated_size, actual_size);
392+
393+ // untar it
394+ QTemporaryDir out;
395+ QDir outdir(out.path());
396+ QFile tarfile(outdir.filePath("tmp.tar"));
397+ tarfile.open(QIODevice::WriteOnly);
398+ tarfile.write(contents.data(), contents.size());
399+ tarfile.close();
400+ QProcess untar;
401+ untar.setWorkingDirectory(outdir.path());
402+ untar.start("tar", QStringList() << "xf" << tarfile.fileName());
403+ EXPECT_TRUE(untar.waitForFinished()) << qPrintable(untar.errorString());
404+
405+ // compare it to the original
406+ EXPECT_FALSE(FileUtils::compareDirectories(in.path(), out.path()));
407+ EXPECT_TRUE(tarfile.remove());
408+ EXPECT_TRUE(FileUtils::compareDirectories(in.path(), out.path()));
409+ }
410 }
411 }
412-
413-// FIXME: calculate compressed size
414-
415-// FIXME: actually build the compressed tar and confirm the size eq calculated size
416-
417-// FIXME: what happens when we pass in a directory name instead of an ordinary file. We need to confirm the subtree gets walked correctly

Subscribers

People subscribed via source and target branches

to all changes: