1 /*
2  * Copyright (C) 1998-2018 ALPS Collaboration. See COPYRIGHT.TXT
3  * All rights reserved. Use is subject to license terms. See LICENSE.TXT
4  * For use in publications, see ACKNOWLEDGE.TXT
5  */
6 
7 #include <vector>
8 #include "alps/params.hpp"
9 #include "gtest/gtest.h"
10 
11 #include "param_generators.hpp"
12 
13 #define ALPS_ASSIGN_PARAM(a_type)  parms[ #a_type ] = static_cast<a_type>(0x41)
14 
15 #define ALPS_TEST_PARAM(a_type) do { a_type x=parms[ #a_type ]; EXPECT_EQ(x,0x41); } while(0)
16 
17 // Used to suppress "unused variable" warning.
dummy_use(const void *)18 static inline void dummy_use(const void*) {};
19 
TEST(param,assignments)20 TEST(param,assignments)
21 {
22     alps::params parms;
23 
24     // ALPS_ASSIGN_PARAM(char);
25     // ALPS_ASSIGN_PARAM(signed char);
26     // ALPS_ASSIGN_PARAM(unsigned char);
27     // ALPS_ASSIGN_PARAM(short);
28     // ALPS_ASSIGN_PARAM(unsigned short);
29     ALPS_ASSIGN_PARAM(int);
30     ALPS_ASSIGN_PARAM(unsigned);
31     ALPS_ASSIGN_PARAM(long);
32     ALPS_ASSIGN_PARAM(unsigned long);
33     // ALPS_ASSIGN_PARAM(long long);
34     // ALPS_ASSIGN_PARAM(unsigned long long);
35     // ALPS_ASSIGN_PARAM(float);
36     ALPS_ASSIGN_PARAM(double);
37     // ALPS_ASSIGN_PARAM(long double);
38 
39     parms["bool"] = true;
40     parms["cstring"] = "asdf";
41     parms["std::string"] = std::string("asdf");
42 
43     std::vector<double> vd(3);
44     vd[0]=1.; vd[1]=2.; vd[2]=4.;
45     parms["dblvec"] = vd;
46 
47     // ALPS_TEST_PARAM(char);
48     // ALPS_TEST_PARAM(signed char);
49     // ALPS_TEST_PARAM(unsigned char);
50     // ALPS_TEST_PARAM(short);
51     // ALPS_TEST_PARAM(unsigned short);
52     ALPS_TEST_PARAM(int);
53     // ALPS_TEST_PARAM(unsigned);
54     ALPS_TEST_PARAM(long);
55     // ALPS_TEST_PARAM(unsigned long);
56     // ALPS_TEST_PARAM(long long);
57     // ALPS_TEST_PARAM(unsigned long long);
58     // ALPS_TEST_PARAM(float);
59     ALPS_TEST_PARAM(double);
60     // ALPS_TEST_PARAM(long double);
61 
62     EXPECT_TRUE(bool(parms["bool"]));
63 
64     EXPECT_EQ(parms["cstring"],std::string("asdf"));
65     EXPECT_EQ(parms["std::string"],std::string("asdf"));
66 
67     EXPECT_EQ(parms["dblvec"],vd);
68 }
69 
TEST(param,StringAssignments)70 TEST(param, StringAssignments)
71 {
72     alps::params p;
73     p["std::string"]=std::string("a string");
74     p["cstring"]="C-string";
75 
76   {
77       EXPECT_THROW({char* s=p["cstring"]; dummy_use(&s);}, alps::params::type_mismatch);
78       EXPECT_THROW({const char* cs=p["cstring"]; dummy_use(&cs);}, alps::params::type_mismatch);
79       EXPECT_THROW({const char* cs=p["std::string"]; dummy_use(&cs);}, alps::params::type_mismatch);
80   }
81 
82   {
83       const char* s1=p["cstring"].as<std::string>().c_str();
84       EXPECT_TRUE(strcmp("C-string",s1)==0);
85 
86       const char* s2=p["std::string"].as<std::string>().c_str();
87       EXPECT_TRUE(strcmp("a string",s2)==0);
88   }
89 
90   {
91       std::string s1=p["cstring"];
92       EXPECT_EQ(s1,"C-string");
93 
94       std::string s2=p["std::string"];
95       EXPECT_EQ(s2,"a string");
96   }
97 
98   // This will not compile:
99   /*
100   {
101     std::string s1; s1=p["cstring"];
102     EXPECT_EQ(s1,"C-string");
103 
104     std::string s2; s2=p["std::string"];
105     EXPECT_EQ(s2,"a string");
106   }
107   */
108 
109   {
110     std::string s1; s1=p["cstring"].as<std::string>();
111     EXPECT_EQ(s1,"C-string");
112 
113     std::string s2; s2=p["std::string"].as<std::string>();
114     EXPECT_EQ(s2,"a string");
115   }
116 }
117 
118 
119 
120 
121 // Testing explicitly assigned/implicitly defined parameters and implicit type conversion
TEST(param,ImplicitDefine)122 TEST(param,ImplicitDefine)
123 {
124     const char* argv[]={"THIS PROGRAM", "--param1=abc", "--param2=4.25"};
125     const int argc=sizeof(argv)/sizeof(*argv);
126 
127     alps::params p(argc,argv);
128 
129     // Explicitly assigning a parameter
130     p["param1"]=3.125;
131     EXPECT_EQ(p["param1"],3.125);
132 
133     {
134         // It is not a string, whatever command line is
135         EXPECT_THROW({const std::string& s=p["param1"]; dummy_use(&s);}, alps::params::type_mismatch);
136 
137         // It is not an int either
138         EXPECT_THROW({int n=p["param1"]; dummy_use(&n);}, alps::params::type_mismatch);
139     }
140 
141     // String cannot be assigned to it
142     EXPECT_THROW(p["param1"]="abc", alps::params::type_mismatch);
143 
144     // Integer can be assigned to it...
145     p["param1"]=3;
146     // ...but it is still not an integer:
147     {
148         EXPECT_THROW({int n=p["param1"]; dummy_use(&n);}, alps::params::type_mismatch);
149     }
150 
151     // It cannot be defined now, once it was assigned
152     EXPECT_THROW((p.define<double>("param1", "Double parameter")), alps::params::extra_definition);
153 
154     // Defining another parameter
155     p.define<double>("param2", "Another double parameter");
156 
157     // Redefinition of the same parameter
158     EXPECT_THROW((p.define<double>("param2", "Double parameter again")), alps::params::double_definition);
159     EXPECT_THROW((p.define<int>("param2", "Int parameter now")), alps::params::double_definition);
160 
161     // Reading the parameter
162     EXPECT_EQ(4.25, p["param2"]);
163 
164     // Assigning to and from the parameter
165 
166     // Assign double --- should work
167     p["param2"]=5.0;
168     EXPECT_EQ(5.0, p["param2"]);
169 
170     // Assign int --- should work, but remain double
171     p["param2"]=5;
172     {
173         EXPECT_THROW({int n=p["param2"]; dummy_use(&n);}, alps::params::type_mismatch);
174     }
175 
176     EXPECT_THROW(p["param2"]="abc", alps::params::type_mismatch);
177 }
178 
179 // Test object invariants: defining a parameter or parsing a file should not affect the visible state
TEST(param,Invariance)180 TEST(param,Invariance)
181 {
182     const char* argv[]={"THIS PROGRAM", "--str=abc", "--int1=111", "--int2=222" };
183     const int argc=sizeof(argv)/sizeof(*argv);
184 
185     alps::params p(argc,argv);
186 
187     p["int3"]=333;
188 
189     // Define the string parameter
190     p.define<std::string>("str","String parameter");
191     // Check the state (triggers parsing!)
192     EXPECT_EQ(333, p["int3"]);
193     EXPECT_EQ(std::string("abc"), p["str"]);
194 
195     // Define the int1 parameter
196     p.define<int>("int1","Int parameter1");
197     // Check the state (triggers parsing!)
198     EXPECT_EQ(std::string("abc"), p["str"]);
199     EXPECT_EQ(111, p["int1"]);
200     EXPECT_EQ(333, p["int3"]);
201 
202     // Change the state
203     p["int3"]=-3333;
204     p["int1"]=-1111;
205 
206     // Define the int2 parameter
207     p.define<int>("int2","Int parameter2");
208     // Check the state (triggers parsing!)
209     EXPECT_EQ(222, p["int2"]);
210     EXPECT_EQ(std::string("abc"), p["str"]);
211     EXPECT_EQ(-1111, p["int1"]);
212     EXPECT_EQ(-3333, p["int3"]);
213 
214     // Define a non-exisiting parameter
215     p.define<int>("int4","Int parameter4 (not provided)");
216     // Check the state (triggers parsing!)
217     EXPECT_EQ(222, p["int2"]);
218     EXPECT_EQ(std::string("abc"), p["str"]);
219     EXPECT_EQ(-1111, p["int1"]);
220     EXPECT_EQ(-3333, p["int3"]);
221 }
222 
223 
224 // FIXME: test type mismatch (on get and on set) systematically for all scalar and vector types
225 
226 
227 // Testing type mismatch exception and message
TEST(param,SetTypeMismatchMessage)228 TEST(param,SetTypeMismatchMessage)
229 {
230     alps::params_ns::testing::CmdlineParameter<int> gen_int("myparam");
231     alps::params p=gen_int.params();
232 
233     // FIXME: probably deserve a separate test
234     EXPECT_TRUE(  p.exists<int>("myparam") );
235     EXPECT_FALSE( p.exists<std::string>("myparam") );
236 
237     bool thrown=false;
238     try {
239       p["myparam"]=1L; // attempt to assing long to an integer parameter
240     } catch (alps::params::type_mismatch& exc) {
241       thrown=true;
242       std::string msg=exc.what();
243       // std::cerr << "DEBUG: msg='" << msg << "'\n";
244       EXPECT_TRUE(msg.find("myparam")!=std::string::npos) << "Option name is not mentioned in exception message: "+msg;
245       EXPECT_TRUE(msg.find("int")!=std::string::npos) << "Option type is not mentioned in exception message: "+msg;
246       EXPECT_TRUE(msg.find("long")!=std::string::npos) << "RHS type is not mentioned in exception message: "+msg;
247     }
248     EXPECT_TRUE(thrown) << "Exception was not thrown!";
249 }
250 
251 // Testing type mismatch exception and message
TEST(param,GetTypeMismatchMessage)252 TEST(param,GetTypeMismatchMessage)
253 {
254     alps::params_ns::testing::CmdlineParameter<long> gen_long("myparam");
255     alps::params p=gen_long.params();
256 
257     bool thrown=false;
258     try {
259       std::string s=p["myparam"]; // attempt to assing integer parameter to a string
260     } catch (alps::params::type_mismatch& exc) {
261       thrown=true;
262       std::string msg=exc.what();
263       // std::cerr << "DEBUG: msg='" << msg << "'\n";
264       EXPECT_TRUE(msg.find("myparam")!=std::string::npos) << "Option name is not mentioned in exception message: "+msg;
265       EXPECT_TRUE(msg.find("long")!=std::string::npos) << "Option type is not mentioned in exception message: "+msg;
266       EXPECT_TRUE(msg.find("std::string")!=std::string::npos) << "LHS type is not mentioned in exception message: "+msg;
267     }
268     EXPECT_TRUE(thrown) << "Exception was not thrown!";
269 }
270