Merge lp:~marcustomlinson/unity-scopes-api/fix_relative_custom_exec_args into lp:unity-scopes-api/devel
- fix_relative_custom_exec_args
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Paweł Stołowski |
Approved revision: | 631 |
Merged at revision: | 631 |
Proposed branch: | lp:~marcustomlinson/unity-scopes-api/fix_relative_custom_exec_args |
Merge into: | lp:unity-scopes-api/devel |
Diff against target: |
407 lines (+184/-90) 8 files modified
include/unity/scopes/internal/Utils.h (+5/-0) scoperegistry/scoperegistry.cpp (+1/-51) src/scopes/internal/Utils.cpp (+83/-0) test/gtest/scopes/Registry/scopes/testscopeB/testscopeB.cpp (+5/-1) test/gtest/scopes/Registry/scopes/testscopeB/testscopeB.ini.in (+1/-1) test/gtest/scopes/internal/Utils/Utils_test.cpp (+86/-0) test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp (+2/-37) unity-scopes.map (+1/-0) |
To merge this branch: | bzr merge lp:~marcustomlinson/unity-scopes-api/fix_relative_custom_exec_args |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paweł Stołowski (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+270777@code.launchpad.net |
Commit message
Loop through each argument of the custom scope runner command and ensure that all path arguments are absolute
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:630
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 631. By Marcus Tomlinson
-
Added tests
Marcus Tomlinson (marcustomlinson) wrote : | # |
> Do you mind adding some tests for convert_
> would be good to have a check for arguments which contain escaped spaces, e.g.
> Foo\ Bar.) in the Utils test
> (test/gtest/
> much better test than with a test scope.
> Other than that looks good and clean!
Added tests.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:631
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : | # |
Awesome, thanks! +1
Preview Diff
1 | === modified file 'include/unity/scopes/internal/Utils.h' |
2 | --- include/unity/scopes/internal/Utils.h 2015-04-10 03:55:25 +0000 |
3 | +++ include/unity/scopes/internal/Utils.h 2015-09-15 07:36:31 +0000 |
4 | @@ -22,6 +22,8 @@ |
5 | #include <string> |
6 | #include <sstream> |
7 | |
8 | +#include <boost/filesystem.hpp> |
9 | + |
10 | namespace unity |
11 | { |
12 | |
13 | @@ -57,6 +59,9 @@ |
14 | |
15 | void make_directories(std::string const& path_name, mode_t mode); |
16 | |
17 | +std::vector<std::string> split_exec_args(std::string const& id, std::string const& custom_exec); |
18 | +std::string convert_exec_rel_to_abs(std::string const& id, boost::filesystem::path const& scope_dir, std::string const& custom_exec); |
19 | + |
20 | } // namespace internal |
21 | |
22 | } // namespace scopes |
23 | |
24 | === modified file 'scoperegistry/scoperegistry.cpp' |
25 | --- scoperegistry/scoperegistry.cpp 2015-07-01 11:02:46 +0000 |
26 | +++ scoperegistry/scoperegistry.cpp 2015-09-15 07:36:31 +0000 |
27 | @@ -259,30 +259,6 @@ |
28 | } |
29 | } |
30 | |
31 | -string get_first_component(string const& id, string const& custom_exec) |
32 | -{ |
33 | - if (custom_exec.empty()) |
34 | - { |
35 | - throw unity::InvalidArgumentException("Invalid scope runner executable for scope: " + id); |
36 | - } |
37 | - |
38 | - wordexp_t exp; |
39 | - string result; |
40 | - |
41 | - // Split command into program and args |
42 | - if (wordexp(custom_exec.c_str(), &exp, WRDE_NOCMD) == 0) |
43 | - { |
44 | - util::ResourcePtr<wordexp_t*, decltype(&wordfree)> free_guard(&exp, wordfree); |
45 | - result = exp.we_wordv[0]; |
46 | - } |
47 | - else |
48 | - { |
49 | - throw unity::InvalidArgumentException("Invalid scope runner executable for scope: " + id); |
50 | - } |
51 | - |
52 | - return result; |
53 | -} |
54 | - |
55 | // For each scope, open the config file for the scope, create the metadata info from the config, |
56 | // and add an entry to the RegistryObject. |
57 | // If the scope uses settings, also parse the settings file and add the settings to the metadata. |
58 | @@ -435,33 +411,7 @@ |
59 | |
60 | try |
61 | { |
62 | - auto custom_exec = sc.scope_runner(); |
63 | - filesystem::path program(get_first_component( scope.first, custom_exec)); |
64 | - if (program.is_relative()) |
65 | - { |
66 | - // First look inside the arch-specific directory |
67 | - if (filesystem::exists(scope_dir / DEB_HOST_MULTIARCH / program)) |
68 | - { |
69 | - // Join the full command, not just the program path |
70 | - exec_data.custom_exec = (scope_dir / DEB_HOST_MULTIARCH / custom_exec).native(); |
71 | - } |
72 | - // Next try in the non arch-aware directory |
73 | - else if (filesystem::exists(scope_dir / program)) |
74 | - { |
75 | - // Join the full command, not just the program path |
76 | - exec_data.custom_exec = (scope_dir / custom_exec).native(); |
77 | - } |
78 | - else |
79 | - { |
80 | - throw unity::InvalidArgumentException( |
81 | - "Nonexistent scope runner '" + custom_exec |
82 | - + "' executable for scope: " + scope.first); |
83 | - } |
84 | - } |
85 | - else |
86 | - { |
87 | - exec_data.custom_exec = custom_exec; |
88 | - } |
89 | + exec_data.custom_exec = convert_exec_rel_to_abs(scope.first, scope_dir, sc.scope_runner()); |
90 | } |
91 | catch (NotFoundException const&) |
92 | { |
93 | |
94 | === modified file 'src/scopes/internal/Utils.cpp' |
95 | --- src/scopes/internal/Utils.cpp 2015-04-10 03:55:25 +0000 |
96 | +++ src/scopes/internal/Utils.cpp 2015-09-15 07:36:31 +0000 |
97 | @@ -19,12 +19,14 @@ |
98 | #include <unity/scopes/internal/Utils.h> |
99 | #include <unity/scopes/ScopeExceptions.h> |
100 | #include <unity/UnityExceptions.h> |
101 | +#include <unity/util/ResourcePtr.h> |
102 | |
103 | #include <boost/filesystem.hpp> |
104 | |
105 | #include <iomanip> |
106 | #include <locale> |
107 | #include <mutex> |
108 | +#include <wordexp.h> |
109 | |
110 | #include <sys/stat.h> |
111 | |
112 | @@ -190,6 +192,87 @@ |
113 | } |
114 | } |
115 | |
116 | +vector<string> split_exec_args(string const& id, string const& custom_exec) |
117 | +{ |
118 | + if (custom_exec.empty()) |
119 | + { |
120 | + throw unity::InvalidArgumentException("Invalid empty executable for scope: '" + id + "'"); |
121 | + } |
122 | + |
123 | + wordexp_t exp; |
124 | + vector<string> result; |
125 | + |
126 | + // Split command into program and args |
127 | + if (wordexp(custom_exec.c_str(), &exp, WRDE_NOCMD) == 0) |
128 | + { |
129 | + util::ResourcePtr<wordexp_t*, decltype(&wordfree)> free_guard(&exp, wordfree); |
130 | + for (size_t i = 0; i < exp.we_wordc; ++i ) |
131 | + { |
132 | + auto argument = string(exp.we_wordv[i]); |
133 | + if(argument.find_first_of(' ') != std::string::npos) |
134 | + { |
135 | + // This argument contains spaces, enclose it in quotation marks |
136 | + result.emplace_back("\"" + argument + "\""); |
137 | + } |
138 | + else |
139 | + { |
140 | + result.emplace_back(argument); |
141 | + } |
142 | + } |
143 | + } |
144 | + else |
145 | + { |
146 | + throw unity::InvalidArgumentException("Invalid executable for scope: '" + id + "'"); |
147 | + } |
148 | + |
149 | + return result; |
150 | +} |
151 | + |
152 | +string convert_exec_rel_to_abs(string const& id, boost::filesystem::path const& scope_dir, string const& custom_exec) |
153 | +{ |
154 | + string result; |
155 | + |
156 | + // Loop through each argument of the scope runner command and ensure all path args are absolute |
157 | + auto custom_exec_args = split_exec_args(id, custom_exec); |
158 | + for (auto custom_exec_arg : custom_exec_args) |
159 | + { |
160 | + boost::filesystem::path argument(custom_exec_arg); |
161 | + if (argument.is_relative()) |
162 | + { |
163 | + // First look inside the arch-specific directory |
164 | + if (boost::filesystem::exists(scope_dir / DEB_HOST_MULTIARCH / argument)) |
165 | + { |
166 | + // Append the argument as a relative path |
167 | + result += (scope_dir / DEB_HOST_MULTIARCH / argument).native() + " "; |
168 | + } |
169 | + // Next try in the non arch-aware directory |
170 | + else if (boost::filesystem::exists(scope_dir / argument)) |
171 | + { |
172 | + // Append the argument as a relative path |
173 | + result += (scope_dir / argument).native() + " "; |
174 | + } |
175 | + // If this is the first argument (program name) it must exist, throw here |
176 | + else if (result.empty()) |
177 | + { |
178 | + throw unity::InvalidArgumentException( |
179 | + "Nonexistent scope runner executable: '" + custom_exec_arg |
180 | + + "' for scope: '" + id + "'"); |
181 | + } |
182 | + // Otherwise just append the argument as is |
183 | + else |
184 | + { |
185 | + result += custom_exec_arg + " "; |
186 | + } |
187 | + } |
188 | + else |
189 | + { |
190 | + result += custom_exec_arg + " "; |
191 | + } |
192 | + } |
193 | + result.resize(result.size() - 1); |
194 | + return result; |
195 | +} |
196 | + |
197 | } // namespace internal |
198 | |
199 | } // namespace scopes |
200 | |
201 | === modified file 'test/gtest/scopes/Registry/scopes/testscopeB/testscopeB.cpp' |
202 | --- test/gtest/scopes/Registry/scopes/testscopeB/testscopeB.cpp 2014-12-05 03:05:37 +0000 |
203 | +++ test/gtest/scopes/Registry/scopes/testscopeB/testscopeB.cpp 2015-09-15 07:36:31 +0000 |
204 | @@ -25,6 +25,9 @@ |
205 | #include <unity/scopes/Runtime.h> |
206 | #include <unity/scopes/PreviewReply.h> |
207 | |
208 | +#include <boost/filesystem.hpp> |
209 | + |
210 | +using namespace boost; |
211 | using namespace std; |
212 | using namespace unity::scopes; |
213 | |
214 | @@ -102,10 +105,11 @@ |
215 | std::string scope_config = argc > 2 ? argv[2] : "FAIL"; |
216 | std::string rt_config = argc > 3 ? argv[3] : "FAIL"; |
217 | std::string unused_arg_2 = argc > 4 ? argv[4] : "FAIL"; |
218 | + filesystem::path abs_path_arg(argc > 5 ? argv[5] : "FAIL"); |
219 | |
220 | // In order to test that custom exec splitting works, we check here that our arbitrary arguments |
221 | // have been delivered to us as expected. |
222 | - if (unused_arg_1 == "unused arg 1" && unused_arg_2 == "unused arg 2") |
223 | + if (unused_arg_1 == "unused arg 1" && unused_arg_2 == "unused arg 2" && filesystem::exists(abs_path_arg)) |
224 | { |
225 | MyScope scope; |
226 | auto runtime = Runtime::create_scope_runtime("testscopeB", rt_config); |
227 | |
228 | === modified file 'test/gtest/scopes/Registry/scopes/testscopeB/testscopeB.ini.in' |
229 | --- test/gtest/scopes/Registry/scopes/testscopeB/testscopeB.ini.in 2014-12-08 15:17:04 +0000 |
230 | +++ test/gtest/scopes/Registry/scopes/testscopeB/testscopeB.ini.in 2015-09-15 07:36:31 +0000 |
231 | @@ -6,5 +6,5 @@ |
232 | Icon = data/scope-B.Icon |
233 | SearchHint = scope-B.SearchHint |
234 | HotKey = scope-B.HotKey |
235 | -ScopeRunner = testscopeB "unused arg 1" %S %R unused' 'arg' '2 |
236 | +ScopeRunner = testscopeB "unused arg 1" %S %R unused' 'arg' '2 ./testscopeB.ini |
237 | IdleTimeout = 2 |
238 | |
239 | === modified file 'test/gtest/scopes/internal/Utils/Utils_test.cpp' |
240 | --- test/gtest/scopes/internal/Utils/Utils_test.cpp 2015-04-10 03:55:25 +0000 |
241 | +++ test/gtest/scopes/internal/Utils/Utils_test.cpp 2015-09-15 07:36:31 +0000 |
242 | @@ -132,3 +132,89 @@ |
243 | |
244 | chmod(child.c_str(), 0777); // Don't leave the dir behind without permissions. |
245 | } |
246 | + |
247 | +TEST(Utils, split_exec_args) |
248 | +{ |
249 | + // Test empty executable |
250 | + try |
251 | + { |
252 | + split_exec_args("test", ""); |
253 | + FAIL(); |
254 | + } |
255 | + catch (std::exception const& e) |
256 | + { |
257 | + EXPECT_STREQ("unity::InvalidArgumentException: Invalid empty executable for scope: 'test'", e.what()); |
258 | + } |
259 | + |
260 | + // Test invalid executable |
261 | + try |
262 | + { |
263 | + split_exec_args("test", "\""); |
264 | + FAIL(); |
265 | + } |
266 | + catch (std::exception const& e) |
267 | + { |
268 | + EXPECT_STREQ("unity::InvalidArgumentException: Invalid executable for scope: 'test'", e.what()); |
269 | + } |
270 | + |
271 | + // Test argument splitting |
272 | + auto exec_args = split_exec_args("test", "/path\\ to/exec' 'file arg \"arg 2\" arg' '3 arg\\ 4"); |
273 | + ASSERT_EQ(5, exec_args.size()); |
274 | + EXPECT_STREQ("\"/path to/exec file\"", exec_args[0].c_str()); |
275 | + EXPECT_STREQ("arg", exec_args[1].c_str()); |
276 | + EXPECT_STREQ("\"arg 2\"", exec_args[2].c_str()); |
277 | + EXPECT_STREQ("\"arg 3\"", exec_args[3].c_str()); |
278 | + EXPECT_STREQ("\"arg 4\"", exec_args[4].c_str()); |
279 | +} |
280 | + |
281 | +TEST(Utils, convert_exec_rel_to_abs) |
282 | +{ |
283 | + // Test empty executable |
284 | + try |
285 | + { |
286 | + convert_exec_rel_to_abs("test", boost::filesystem::path(TEST_DIR), ""); |
287 | + FAIL(); |
288 | + } |
289 | + catch (std::exception const& e) |
290 | + { |
291 | + EXPECT_STREQ("unity::InvalidArgumentException: Invalid empty executable for scope: 'test'", e.what()); |
292 | + } |
293 | + |
294 | + // Test invalid executable |
295 | + try |
296 | + { |
297 | + convert_exec_rel_to_abs("test", boost::filesystem::path(TEST_DIR), "\""); |
298 | + FAIL(); |
299 | + } |
300 | + catch (std::exception const& e) |
301 | + { |
302 | + EXPECT_STREQ("unity::InvalidArgumentException: Invalid executable for scope: 'test'", e.what()); |
303 | + } |
304 | + |
305 | + // Test nonexistent executable |
306 | + try |
307 | + { |
308 | + convert_exec_rel_to_abs("test", boost::filesystem::path(TEST_DIR), "noexec"); |
309 | + FAIL(); |
310 | + } |
311 | + catch (std::exception const& e) |
312 | + { |
313 | + EXPECT_STREQ("unity::InvalidArgumentException: Nonexistent scope runner executable: 'noexec' for scope: 'test'", e.what()); |
314 | + } |
315 | + |
316 | + // Test absolute executable |
317 | + auto abs_exec = convert_exec_rel_to_abs("test", boost::filesystem::path(TEST_DIR), "/bin/bash"); |
318 | + EXPECT_STREQ("/bin/bash", abs_exec.c_str()); |
319 | + |
320 | + // Test absolute executable w/ args |
321 | + abs_exec = convert_exec_rel_to_abs("test", boost::filesystem::path(TEST_DIR), "/bin/bash arg \"arg 2\" arg' '3 arg\\ 4"); |
322 | + EXPECT_STREQ("/bin/bash arg \"arg 2\" \"arg 3\" \"arg 4\"", abs_exec.c_str()); |
323 | + |
324 | + // Test relative executable (w/ ./) |
325 | + abs_exec = convert_exec_rel_to_abs("test", boost::filesystem::path(TEST_DIR), "./Utils_test"); |
326 | + EXPECT_TRUE(boost::filesystem::exists(abs_exec)); |
327 | + |
328 | + // Test relative executable (w/o ./) |
329 | + abs_exec = convert_exec_rel_to_abs("test", boost::filesystem::path(TEST_DIR), "Utils_test"); |
330 | + EXPECT_TRUE(boost::filesystem::exists(abs_exec)); |
331 | +} |
332 | |
333 | === modified file 'test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp' |
334 | --- test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp 2015-07-22 03:54:35 +0000 |
335 | +++ test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp 2015-09-15 07:36:31 +0000 |
336 | @@ -28,6 +28,7 @@ |
337 | #include <unity/scopes/internal/ScopeMetadataImpl.h> |
338 | #include <unity/scopes/internal/ScopeImpl.h> |
339 | #include <unity/scopes/internal/UniqueID.h> |
340 | +#include <unity/scopes/internal/Utils.h> |
341 | #include <unity/scopes/internal/zmq_middleware/ZmqRegistry.h> |
342 | #include <unity/scopes/SearchMetadata.h> |
343 | #include <unity/scopes/ScopeExceptions.h> |
344 | @@ -465,42 +466,6 @@ |
345 | return count_child_procs() - start_process_count; |
346 | } |
347 | |
348 | - string convert_custom_exec(fs::path const& scope_dir, string const& custom_exec) |
349 | - { |
350 | - string result; |
351 | - |
352 | - vector<string> split; |
353 | - boost::split(split, custom_exec, boost::is_space()); |
354 | - fs::path program(split.front()); |
355 | - if (program.is_relative()) |
356 | - { |
357 | - // First look inside the arch-specific directory |
358 | - if (fs::exists(scope_dir / DEB_HOST_MULTIARCH / program)) |
359 | - { |
360 | - // Join the full command, not just the program path |
361 | - result = (scope_dir / DEB_HOST_MULTIARCH / custom_exec).native(); |
362 | - } |
363 | - // Next try in the non arch-aware directory |
364 | - else if (fs::exists(scope_dir / program)) |
365 | - { |
366 | - // Join the full command, not just the program path |
367 | - result = (scope_dir / custom_exec).native(); |
368 | - } |
369 | - else |
370 | - { |
371 | - throw unity::InvalidArgumentException( |
372 | - "Nonexistent scope runner '" + custom_exec |
373 | - + "' executable for scope"); |
374 | - } |
375 | - } |
376 | - else |
377 | - { |
378 | - result = custom_exec; |
379 | - } |
380 | - |
381 | - return result; |
382 | - } |
383 | - |
384 | ScopeProxy start_testscopeB() |
385 | { |
386 | std::string test_scope_id = "testscopeB"; |
387 | @@ -519,7 +484,7 @@ |
388 | |
389 | RegistryObject::ScopeExecData exec_data; |
390 | exec_data.scope_id = test_scope_id; |
391 | - exec_data.custom_exec = convert_custom_exec(REGISTRY_TEST_DIR "/scopes/testscopeB", sc.scope_runner()); |
392 | + exec_data.custom_exec = convert_exec_rel_to_abs(test_scope_id, REGISTRY_TEST_DIR "/scopes/testscopeB", sc.scope_runner()); |
393 | exec_data.runtime_config = runtime_ini; |
394 | exec_data.scope_config = test_scope_config; |
395 | exec_data.timeout_ms = process_timeout; |
396 | |
397 | === modified file 'unity-scopes.map' |
398 | --- unity-scopes.map 2015-04-10 03:55:25 +0000 |
399 | +++ unity-scopes.map 2015-09-15 07:36:31 +0000 |
400 | @@ -13,6 +13,7 @@ |
401 | virtual?thunk?to?unity::scopes::[!iu]*; |
402 | vtable?for?unity::scopes::utility::[!i]*; |
403 | vtable?for?unity::scopes::[!iu]*; |
404 | + unity::scopes::internal::convert_exec_rel_to_abs*; |
405 | unity::scopes::internal::Executor::*; |
406 | unity::scopes::internal::IniSettingsSchema::*; |
407 | unity::scopes::internal::JsonSettingsSchema::*; |
Do you mind adding some tests for convert_ exec_rel_ to_abs (and split_args - would be good to have a check for arguments which contain escaped spaces, e.g. Foo\ Bar.) in the Utils test (test/gtest/ scopes/ internal/ Utils/Utils_ test.cpp) ? That should allow for a much better test than with a test scope.
Other than that looks good and clean!