1 //
2 // Copyright (c) 2017 Benjamin Kaufmann
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to
6 // deal in the Software without restriction, including without limitation the
7 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 // sell copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 // IN THE SOFTWARE.
21 #include "catch.hpp"
22 #include <potassco/program_opts/value_store.h>
23 #include <potassco/program_opts/mapped_value.h>
24 #include <potassco/program_opts/typed_value.h>
25 #include <string.h>
26 #include <memory>
27 #include <map>
28 namespace Potassco {
29 namespace ProgramOptions {
30 namespace Test {
31 namespace Po = ProgramOptions;
32 struct Counted {
33 	static int count;
34 	int parsed;
CountedPotassco::ProgramOptions::Test::Counted35 	Counted() : parsed(0) { ++count; }
~CountedPotassco::ProgramOptions::Test::Counted36 	~Counted() { --count; }
CountedPotassco::ProgramOptions::Test::Counted37 	Counted(const Counted& o) : parsed(o.parsed) { ++count; }
parsePotassco::ProgramOptions::Test::Counted38 	static bool parse(const std::string&, Counted& out) {
39 		++out.parsed;
40 		return true;
41 	}
42 };
43 class ValuePtr {
44 public:
ValuePtr(Po::Value * p)45 	explicit ValuePtr(Po::Value* p) : ptr_(p) {}
~ValuePtr()46 	~ValuePtr() { delete ptr_; }
operator ->() const47 	Po::Value* operator->() const { return ptr_; }
48 private:
49 	ValuePtr(const ValuePtr&);
50 	ValuePtr& operator=(const ValuePtr&);
51 	Po::Value* ptr_;
52 };
53 
54 int Counted::count = 0;
55 struct Notified {
NotifiedPotassco::ProgramOptions::Test::Notified56 	Notified() : notifications(0), reused(0), loc(0), ret(false) {}
~NotifiedPotassco::ProgramOptions::Test::Notified57 	~Notified() { delete loc; }
notifyIntPotassco::ProgramOptions::Test::Notified58 	static bool notifyInt(Notified* this_, const std::string& name, const int* loc) {
59 		++this_->notifications;
60 		this_->opts.insert(std::map<std::string, int>::value_type(name, *loc));
61 		if (this_->loc == loc) {
62 			++this_->reused;
63 		}
64 		if (this_->ret) {
65 			this_->loc = loc;
66 		}
67 		return this_->ret;
68 	}
notifyUntypedPotassco::ProgramOptions::Test::Notified69 	static bool notifyUntyped(Notified* this_, const std::string& name, const std::string& value) {
70 		++this_->notifications;
71 		int temp;
72 		if (Potassco::string_cast<int>(value, temp)) {
73 			this_->opts[name] = temp;
74 			return true;
75 		}
76 		return false;
77 	}
78 	std::map<std::string, int> opts;
79 	int notifications, reused;
80 	const int* loc;
81 	bool ret;
82 };
83 
84 TEST_CASE("Test value store", "[value]") {
85 	SECTION("default is empty") {
86 		Po::ValueStore x;
87 		REQUIRE(x.empty());
88 	}
89 	SECTION("values are copied into store") {
90 		Po::ValueStore x;
91 		Po::ValueStore y((Counted()));
92 		x = y;
93 		REQUIRE(Counted::count == 2);
94 	}
95 	SECTION("store supports swap") {
96 		Po::ValueStore x((Counted())), y((Counted()));
97 		Po::value_cast<Counted>(x).parsed = 0;
98 		Po::value_cast<Counted>(y).parsed = 10;
99 		x.swap(y);
100 		REQUIRE(Po::value_cast<Counted>(x).parsed == 10);
101 		REQUIRE(Po::value_cast<Counted>(y).parsed == 0);
102 		x.clear();
103 		REQUIRE(Po::unsafe_value_cast<Counted>(&x) == static_cast<Counted*>(0));
104 	}
105 	REQUIRE(Counted::count == 0);
106 }
107 
108 TEST_CASE("Test flag", "[value]") {
109 	bool loud;
110 	SECTION("check properties") {
111 		ValuePtr loudFlag(Po::flag(loud));
112 		REQUIRE(loudFlag->isImplicit() == true);
113 		REQUIRE(loudFlag->isFlag() == true);
114 		REQUIRE(strcmp(loudFlag->implicit(), "1") == 0);
115 	}
116 	SECTION("default parser stores true") {
117 		ValuePtr loudFlag(Po::flag(loud));
118 		REQUIRE((loudFlag->parse("", "") && loud == true));
119 		loud = false;
120 		loudFlag->parse("", "on");
121 		REQUIRE(loud == true);
122 	}
123 	SECTION("alternative parser can store false") {
124 		ValuePtr quietFlag(Po::flag(loud, Po::store_false));
125 		REQUIRE((quietFlag->parse("", "") && loud == false));
126 		quietFlag->parse("", "off");
127 		REQUIRE(loud == true);
128 	}
129 }
130 
131 TEST_CASE("Test storeTo", "[value]") {
132 	int x; bool y;
133 	ValuePtr v1(Po::storeTo(x));
134 	ValuePtr v2(Po::flag(y));
135 	SECTION("store int") {
136 		REQUIRE(v1->parse("", "22"));
137 		REQUIRE(x == 22);
138 	}
139 	SECTION("fail on invalid type") {
140 		x = 99;
141 		REQUIRE(!v1->parse("", "ab"));
142 		REQUIRE(x == 99);
143 	}
144 	SECTION("init with state") {
145 		ValuePtr v(Po::storeTo(x)->state(Po::Value::value_defaulted));
146 		REQUIRE(v->state() == Po::Value::value_defaulted);
147 		REQUIRE((v2->state() == Po::Value::value_unassigned && v2->isImplicit() && v2->isFlag()));
148 	}
149 	SECTION("parse as default") {
150 		REQUIRE(v2->parse("", "off", Po::Value::value_defaulted));
151 		REQUIRE(v2->state() == Po::Value::value_defaulted);
152 	}
153 	SECTION("parse bool as implicit") {
154 		REQUIRE(v2->parse("", ""));
155 		REQUIRE(y == true);
156 		v2->implicit("0");
157 		REQUIRE(v2->parse("", ""));
158 		REQUIRE(y == false);
159 	}
160 	SECTION("parse int as implicit") {
161 		v1->implicit(LIT_TO_STRING(102));
162 		REQUIRE(v1->isImplicit());
163 		REQUIRE((v1->parse("", "") && x == 102));
164 	}
165 
166 	SECTION("test custom parser") {
167 		Counted c;
168 		ValuePtr vc(Po::storeTo(c, &Counted::parse)->implicit(""));
169 		REQUIRE(vc->parse("", ""));
170 		REQUIRE(c.parsed == 1);
171 	}
172 }
173 
174 TEST_CASE("Test custom value", "[value]") {
175 	Notified n;
176 	SECTION("with typed value creation") {
177 		ValuePtr v(Po::notify<int>(&n, &Notified::notifyInt));
178 		v->parse("foo", "123");
179 		n.ret = true;
180 		v->parse("bar", "342");
181 		v->parse("jojo", "999");
182 		REQUIRE(n.notifications == 3);
183 		REQUIRE(n.reused == 1);
184 		REQUIRE(n.opts["foo"] == 123);
185 		REQUIRE(n.opts["bar"] == 342);
186 		REQUIRE(*n.loc == 999);
187 	}
188 	SECTION("with untyped value") {
189 		ValuePtr v(Po::notify(&n, &Notified::notifyUntyped));
190 		REQUIRE(v->parse("foo", "123"));
191 		REQUIRE(v->parse("bar", "342"));
192 		REQUIRE(v->parse("jojo", "999"));
193 		REQUIRE(!v->parse("kaputt", "x12"));
194 		REQUIRE(n.reused == 0);
195 		REQUIRE(n.notifications == 4);
196 		REQUIRE(n.opts["foo"] == 123);
197 		REQUIRE(n.opts["bar"] == 342);
198 		REQUIRE(n.opts["jojo"] == 999);
199 		REQUIRE(n.opts.count("kaputt") == 0);
200 	}
201 }
202 TEST_CASE("Test mapped value", "[value]") {
203 	Po::ValueMap vm;
204 	ValuePtr v1(Po::store<int>(vm));
205 	ValuePtr v2(Po::store<double>(vm));
206 	ValuePtr v3(Po::flag(vm));
207 	v1->parse("foo", "22");
208 	v2->parse("bar", "99.2");
209 	v3->parse("help", "false");
210 	REQUIRE(Po::value_cast<int>(vm["foo"]) == 22);
211 	REQUIRE(Po::value_cast<double>(vm["bar"]) == 99.2);
212 	REQUIRE(Po::value_cast<bool>(vm["help"]) == false);
213 
214 	v1->parse("foo", "27");
215 	REQUIRE(Po::value_cast<int>(vm["foo"]) == 27);
216 }
217 struct Color { enum Value { RED = 2, GREEN = 10, BLUE = 20 }; };
218 struct Mode  { enum Value { DEF, IMP, EXP }; };
219 TEST_CASE("Test enum value", "[value]") {
220 	int x;
221 	Mode::Value y;
222 
223 	ValuePtr v1(Po::storeTo(x, Po::values<Color::Value>()
224 		("Red", Color::RED)
225 		("Green", Color::GREEN)
226 		("Blue", Color::BLUE)));
227 
228 	ValuePtr v2(Po::storeTo(y, Po::values<Mode::Value>()
229 		("Default", Mode::DEF)
230 		("Implicit", Mode::IMP)
231 		("Explicit", Mode::EXP)));
232 
233 	REQUIRE((v1->parse("", "Red") && x == 2));
234 	REQUIRE((v1->parse("", "GREEN") && x == Color::GREEN));
235 	REQUIRE(!v1->parse("", "Blu"));
236 
237 	REQUIRE((v2->parse("", "Implicit") && y == Mode::IMP));
238 }
239 
240 }}}
241