Mir

Merge lp:~alan-griffiths/mir/command-line-handling into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 1826
Proposed branch: lp:~alan-griffiths/mir/command-line-handling
Merge into: lp:mir
Diff against target: 290 lines (+167/-6)
9 files modified
include/platform/mir/options/default_configuration.h (+5/-0)
include/platform/mir/options/program_option.h (+3/-0)
server-ABI-sha1sums (+2/-2)
src/platform/options/default_configuration.cpp (+27/-0)
src/platform/options/program_option.cpp (+9/-2)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_command_line_handling.cpp (+96/-0)
tests/acceptance-tests/test_nested_mir.cpp (+2/-2)
tests/unit-tests/options/test_program_option.cpp (+22/-0)
To merge this branch: bzr merge lp:~alan-griffiths/mir/command-line-handling
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Abstain
Robert Carr (community) Approve
Alexandros Frantzis (community) Approve
Review via email: mp+229255@code.launchpad.net

Commit message

config: make it easier to separate command line options used by Mir from those used elsewhere

Description of the change

config: make it easier to separate command line options used by Mir from those used elsewhere

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Revision history for this message
Robert Carr (robertcarr) wrote :

Looks ok to me. I am not sure that std::function callback is the best API. Certainly

bla_init(int *argc, char ***argv)

is most popular in our ecosystem. So people will use like:

gst_init(argc, argv)
gtk_init(argc, argv)
myapp_init(argc, argv)

I guess server configuration construction is a little different though and I am open to a new pattern.

review: Approve
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Looks ok to me. I am not sure that std::function callback is the best API.
> Certainly
>
> bla_init(int *argc, char ***argv)
>
> is most popular in our ecosystem. So people will use like:
>
> gst_init(argc, argv)
> gtk_init(argc, argv)
> myapp_init(argc, argv)
>
> I guess server configuration construction is a little different though and I
> am open to a new pattern.

Well, it isn't hard to do blah_init(unparsed_arguments.size(), unparsed_arguments.data()) in the handler. But I guess I could also do that outside the call.

What do people think the best?

function<void(int *argc, char const* const* argv)> or function<void(std::vector<char const*> const&)>

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

*Needs Discussion*

> > Looks ok to me. I am not sure that std::function callback is the best API.
> > Certainly
> >
> > bla_init(int *argc, char ***argv)
> >
> > is most popular in our ecosystem. So people will use like:
> >
> > gst_init(argc, argv)
> > gtk_init(argc, argv)
> > myapp_init(argc, argv)
> >
> > I guess server configuration construction is a little different though and I
> > am open to a new pattern.
>
> Well, it isn't hard to do blah_init(unparsed_arguments.size(),
> unparsed_arguments.data()) in the handler. But I guess I could also do that
> outside the call.
>
> What do people think the best?
>
> function<void(int *argc, char const* const* argv)> or
> function<void(std::vector<char const*> const&)>

review: Needs Information
Revision history for this message
Gerry Boland (gerboland) wrote :

I'd vote for function<void(int *argc, char const* const* argv)> - just as it's what other toolkits expect.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> I'd vote for function<void(int *argc, char const* const* argv)> - just as it's
> what other toolkits expect.

Done

review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Start 6: mir_acceptance_tests
> Build timed out (after 240 minutes). Marking the build as failed.
> java.lang.InterruptedException
> Build step 'Debian PBuilder NG' marked build as failure
> Archiving artifacts
> Recording test results
> Build was marked for publishing on https://jenkins.qa.ubuntu.com/
> Finished: FAILURE

Do we have hanging tests? Re-approving as this is unlikely to be related.

Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/platform/mir/options/default_configuration.h'
2--- include/platform/mir/options/default_configuration.h 2014-04-15 14:47:19 +0000
3+++ include/platform/mir/options/default_configuration.h 2014-08-05 15:50:09 +0000
4@@ -30,6 +30,10 @@
5 {
6 public:
7 DefaultConfiguration(int argc, char const* argv[]);
8+ DefaultConfiguration(
9+ int argc,
10+ char const* argv[],
11+ std::function<void(int argc, char const* const* argv)> const& handler);
12 virtual ~DefaultConfiguration() = default;
13
14 // add_options() allows users to add their own options. This MUST be called
15@@ -57,6 +61,7 @@
16
17 int const argc;
18 char const** const argv;
19+ std::function<void(int argc, char const* const* argv)> const unparsed_arguments_handler;
20 std::shared_ptr<boost::program_options::options_description> const program_options;
21 std::shared_ptr<Option> mutable options;
22 };
23
24=== modified file 'include/platform/mir/options/program_option.h'
25--- include/platform/mir/options/program_option.h 2014-03-06 06:05:17 +0000
26+++ include/platform/mir/options/program_option.h 2014-08-05 15:50:09 +0000
27@@ -54,8 +54,11 @@
28 boost::any const& get(char const* name) const override;
29 using Option::get;
30
31+ std::vector<std::string> unparsed_command_line() const;
32+
33 private:
34 boost::program_options::variables_map options;
35+ std::vector<std::string> unparsed_tokens;
36 };
37 }
38 }
39
40=== modified file 'server-ABI-sha1sums'
41--- server-ABI-sha1sums 2014-08-04 15:54:08 +0000
42+++ server-ABI-sha1sums 2014-08-05 15:50:09 +0000
43@@ -37,9 +37,9 @@
44 15f201741a465de33e55ffc1ea775b507a5be950 include/platform/mir/graphics/renderable.h
45 4b640ec72b04cc2de7671afd7c78942fb9e775a7 include/platform/mir/graphics/tessellation_helpers.h
46 3b5be510366571c992e0927cc5662af9b0611ae1 include/platform/mir/options/configuration.h
47-5879da8056f9a4ff8960f586d27117c87b110b2a include/platform/mir/options/default_configuration.h
48+3a961c5e85b0b9d20eadddd938677776288c06c6 include/platform/mir/options/default_configuration.h
49 b45f14082c4f8b29efaa1b13de795dcb29deb738 include/platform/mir/options/option.h
50-f42afacda33e598f9c29f2f5b72932f75dd7d27c include/platform/mir/options/program_option.h
51+1133726e66a246bd3988616e205e186278a64731 include/platform/mir/options/program_option.h
52 5d0213652adc4821a2a180ced74808ca0bbf2c45 include/platform/mir/shared_library_loader.h
53 505444aeb61d0f55893a7ba0998b25c2041f76c4 include/server/mir/asio_main_loop.h
54 a4c8f0b0c30784ea6930a8ee6651f1d6efa47231 include/server/mir/compositor/buffer_stream.h
55
56=== modified file 'src/platform/options/default_configuration.cpp'
57--- src/platform/options/default_configuration.cpp 2014-07-09 10:48:47 +0000
58+++ src/platform/options/default_configuration.cpp 2014-08-05 15:50:09 +0000
59@@ -63,8 +63,29 @@
60 }
61
62 mo::DefaultConfiguration::DefaultConfiguration(int argc, char const* argv[]) :
63+ DefaultConfiguration(
64+ argc, argv,
65+ [](int argc, char const* const* argv)
66+ {
67+ if (argc)
68+ {
69+ std::ostringstream help_text;
70+ help_text << "Unknown command line options:";
71+ for (auto opt = argv; opt != argv+argc ; ++opt)
72+ help_text << ' ' << *opt;
73+ BOOST_THROW_EXCEPTION(mir::AbnormalExit(help_text.str()));
74+ }
75+ })
76+{
77+}
78+
79+mo::DefaultConfiguration::DefaultConfiguration(
80+ int argc,
81+ char const* argv[],
82+ std::function<void(int argc, char const* const* argv)> const& handler) :
83 argc(argc),
84 argv(argv),
85+ unparsed_arguments_handler{handler},
86 program_options(std::make_shared<boost::program_options::options_description>(
87 "Command-line options.\n"
88 "Environment variables capitalise long form with prefix \"MIR_SERVER_\" and \"_\" in place of \"-\""))
89@@ -187,6 +208,12 @@
90
91 options.parse_arguments(desc, argc, argv);
92
93+ auto const unparsed_arguments = options.unparsed_command_line();
94+ std::vector<char const*> tokens;
95+ for (auto const& token : unparsed_arguments)
96+ tokens.push_back(token.c_str());
97+ unparsed_arguments_handler(tokens.size(), tokens.data());
98+
99 if (options.is_set("help"))
100 {
101 std::ostringstream help_text;
102
103=== modified file 'src/platform/options/program_option.cpp'
104--- src/platform/options/program_option.cpp 2014-03-06 06:05:17 +0000
105+++ src/platform/options/program_option.cpp 2014-08-05 15:50:09 +0000
106@@ -43,9 +43,16 @@
107 int argc,
108 char const* argv[])
109 {
110- // TODO: Don't allow unregistered options, once we allow better overriding of option parsing
111- po::store(po::command_line_parser(argc, argv).options(desc).allow_unregistered().run(), options);
112+ auto parsed_command_line = po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
113+ po::store(parsed_command_line, options);
114 po::notify(options);
115+
116+ unparsed_tokens = collect_unrecognized(parsed_command_line.options, po::include_positional);
117+}
118+
119+std::vector<std::string> mo::ProgramOption::unparsed_command_line() const
120+{
121+ return unparsed_tokens;
122 }
123
124 void mo::ProgramOption::parse_environment(
125
126=== modified file 'tests/acceptance-tests/CMakeLists.txt'
127--- tests/acceptance-tests/CMakeLists.txt 2014-07-30 15:15:16 +0000
128+++ tests/acceptance-tests/CMakeLists.txt 2014-08-05 15:50:09 +0000
129@@ -18,6 +18,7 @@
130 test_client_library.cpp
131 test_client_library_old.cpp
132 test_client_surface_events.cpp
133+ test_command_line_handling.cpp
134 test_custom_input_dispatcher.cpp
135 test_client_surfaces.cpp
136 test_test_framework.cpp
137
138=== added file 'tests/acceptance-tests/test_command_line_handling.cpp'
139--- tests/acceptance-tests/test_command_line_handling.cpp 1970-01-01 00:00:00 +0000
140+++ tests/acceptance-tests/test_command_line_handling.cpp 2014-08-05 15:50:09 +0000
141@@ -0,0 +1,96 @@
142+/*
143+ * Copyright © 2014 Canonical Ltd.
144+ *
145+ * This program is free software: you can redistribute it and/or modify it
146+ * under the terms of the GNU General Public License version 3,
147+ * as published by the Free Software Foundation.
148+ *
149+ * This program is distributed in the hope that it will be useful,
150+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
151+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
152+ * GNU General Public License for more details.
153+ *
154+ * You should have received a copy of the GNU General Public License
155+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
156+ *
157+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
158+ */
159+
160+#include "mir/run_mir.h"
161+#include "mir/default_server_configuration.h"
162+#include "mir/options/default_configuration.h"
163+
164+#include <gtest/gtest.h>
165+#include <gmock/gmock.h>
166+
167+using namespace testing;
168+
169+namespace mo = mir::options;
170+
171+struct CommandLineHandling : Test
172+{
173+ MOCK_METHOD1(callback, void(std::vector<char const*> const&));
174+
175+ std::function<void(int argc, char const* const* argv)> callback_functor =
176+ [this](int argc, char const* const* argv)
177+ {
178+ // Copy to a vector as ElementsAre() is convenient for checking
179+ std::vector<char const*> const copy{argv, argv+argc};
180+ callback(copy);
181+ };
182+
183+ void invoke_parsing(mo::Configuration& config) { config.the_options(); }
184+
185+};
186+
187+TEST_F(CommandLineHandling, valid_options_are_accepted_by_default_callback)
188+{
189+ char const* argv[] =
190+ { "dummy-exe-name", "--file", "test", "--enable-input", "off"};
191+
192+ int const argc = std::distance(std::begin(argv), std::end(argv));
193+
194+ mo::DefaultConfiguration config{argc, argv};
195+
196+ invoke_parsing(config);
197+}
198+
199+TEST_F(CommandLineHandling, unrecognised_tokens_cause_default_callback_to_throw)
200+{
201+ char const* argv[] =
202+ { "dummy-exe-name", "--file", "test", "--hello", "world", "--enable-input", "off"};
203+
204+ int const argc = std::distance(std::begin(argv), std::end(argv));
205+
206+ mo::DefaultConfiguration config{argc, argv};
207+
208+ EXPECT_THROW(invoke_parsing(config), std::runtime_error);
209+}
210+
211+TEST_F(CommandLineHandling, valid_options_are_not_passed_to_callback)
212+{
213+ char const* argv[] =
214+ { "dummy-exe-name", "--file", "test", "--enable-input", "off"};
215+
216+ int const argc = std::distance(std::begin(argv), std::end(argv));
217+
218+ EXPECT_CALL(*this, callback(ElementsAre()));
219+
220+ mo::DefaultConfiguration config{argc, argv, callback_functor};
221+
222+ invoke_parsing(config);
223+}
224+
225+TEST_F(CommandLineHandling, unrecognised_tokens_are_passed_to_callback)
226+{
227+ char const* argv[] =
228+ { "dummy-exe-name", "--file", "test", "--hello", "world", "--enable-input", "off"};
229+
230+ int const argc = std::distance(std::begin(argv), std::end(argv));
231+
232+ EXPECT_CALL(*this, callback(ElementsAre(StrEq("--hello"), StrEq("world"))));
233+
234+ mo::DefaultConfiguration config{argc, argv, callback_functor};
235+
236+ invoke_parsing(config);
237+}
238
239=== modified file 'tests/acceptance-tests/test_nested_mir.cpp'
240--- tests/acceptance-tests/test_nested_mir.cpp 2014-07-21 03:35:31 +0000
241+++ tests/acceptance-tests/test_nested_mir.cpp 2014-08-05 15:50:09 +0000
242@@ -92,13 +92,13 @@
243
244 struct FakeCommandLine
245 {
246- static int const argc = 6;
247+ static int const argc = 7;
248 char const* argv[argc];
249
250 FakeCommandLine(std::string const& host_socket)
251 {
252 char const** to = argv;
253- for(auto from : { "--file", "NestedServer", "--host-socket", host_socket.c_str(), "--enable-input", "off"})
254+ for(auto from : { "dummy-exe-name", "--file", "NestedServer", "--host-socket", host_socket.c_str(), "--enable-input", "off"})
255 {
256 *to++ = from;
257 }
258
259=== modified file 'tests/unit-tests/options/test_program_option.cpp'
260--- tests/unit-tests/options/test_program_option.cpp 2014-03-06 06:05:17 +0000
261+++ tests/unit-tests/options/test_program_option.cpp 2014-08-05 15:50:09 +0000
262@@ -192,6 +192,28 @@
263 EXPECT_THROW(po.get<int>("flag-yes,y"), std::bad_cast);
264 }
265
266+TEST_F(ProgramOption, unparsed_command_line_returns_unprocessed_tokens)
267+{
268+ using namespace testing;
269+
270+ mir::options::ProgramOption po;
271+
272+ const int argc = 11;
273+ char const* argv[argc] = {
274+ __PRETTY_FUNCTION__,
275+ "--flag-yes", "yes",
276+ "--hello",
277+ "-f", "test_file",
278+ "world",
279+ "-c", "27",
280+ "--answer", "42"
281+ };
282+
283+ po.parse_arguments(desc, argc, argv);
284+
285+ EXPECT_THAT(po.unparsed_command_line(), ElementsAre("--hello", "world", "--answer", "42"));
286+}
287+
288 TEST(ProgramOptionEnv, parse_environment)
289 {
290 // Env variables should be uppercase and "_" delimited

Subscribers

People subscribed via source and target branches