Skip to content

Commit 97bfb9a

Browse files
committed
[test] add ROOT sniffer testing
Verify execution of several supported requests which can be handled by http server. Testing: - root.json - root.xml - file.root - exe.json - exe.json with POST data - item.json - cmd.json - multi.json Also verify basic functionality of TRootSniffer::DecodeUrlOptionValue method
1 parent d9aca6d commit 97bfb9a

3 files changed

Lines changed: 347 additions & 3 deletions

File tree

net/httpsniff/CMakeLists.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
# Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.
1+
# Copyright (C) 1995-2026, Rene Brun and Fons Rademakers.
22
# All rights reserved.
33
#
44
# For the licensing terms see $ROOTSYS/LICENSE.
55
# For the list of contributors see $ROOTSYS/README/CREDITS.
66

77
############################################################################
8-
# CMakeLists.txt file for building ROOT net/http package
9-
# @author Pere Mato, CERN
8+
# CMakeLists.txt file for building ROOT net/httpsniff package
9+
# @author Sergey Linev, GSI
1010
############################################################################
1111

1212
ROOT_STANDARD_LIBRARY_PACKAGE(RHTTPSniff
@@ -24,3 +24,5 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RHTTPSniff
2424
Tree
2525
XMLIO
2626
)
27+
28+
ROOT_ADD_TEST_SUBDIRECTORY(test)

net/httpsniff/test/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (C) 1995-2026, Rene Brun and Fons Rademakers.
2+
# All rights reserved.
3+
#
4+
# For the licensing terms see $ROOTSYS/LICENSE.
5+
# For the list of contributors see $ROOTSYS/README/CREDITS.
6+
7+
############################################################################
8+
# CMakeLists.txt file for building ROOT net/http package
9+
# @author Sergey Linev, GSI
10+
############################################################################
11+
12+
ROOT_ADD_GTEST(testRootSniffer test_sniffer.cxx LIBRARIES RHTTPSniff)
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
#include "gtest/gtest.h"
2+
3+
#include <string>
4+
5+
#include "TNamed.h"
6+
#include "TH1.h"
7+
#include "TBufferJSON.h"
8+
#include "THttpCallArg.h"
9+
#include "TROOT.h"
10+
#include "TRootSnifferFull.h"
11+
12+
// simple class to access protected method
13+
14+
class TDecodeTest : public TRootSniffer {
15+
public:
16+
TDecodeTest() : TRootSniffer("sniffer") {}
17+
18+
std::string Decode(const char *value, Bool_t remove_quotes = kTRUE)
19+
{
20+
TString res = DecodeUrlOptionValue(value, remove_quotes);
21+
return res.Data();
22+
}
23+
};
24+
25+
// check basic URL parameters decoding
26+
TEST(TRootSniffer, decode_url_options)
27+
{
28+
TDecodeTest test;
29+
30+
EXPECT_EQ(test.Decode(""), "");
31+
32+
// single quote has to be escaped
33+
EXPECT_EQ(test.Decode("\""), "\\\"");
34+
35+
// single backalsh has to be escaped
36+
EXPECT_EQ(test.Decode("\\"), "\\\\");
37+
38+
// remove quotes
39+
EXPECT_EQ(test.Decode("\"\""), "");
40+
41+
// remove quotes and escape quotes
42+
EXPECT_EQ(test.Decode("\"\"\""), "\\\"");
43+
44+
// remove quotes and escape backslah
45+
EXPECT_EQ(test.Decode("\"\\\""), "\\\\");
46+
47+
// remove quotes and remove special charsescape backslah
48+
EXPECT_EQ(test.Decode("\"abc\njkl\t\""), "abcjkl");
49+
50+
// escape quotes in the middle
51+
EXPECT_EQ(test.Decode("someFunc(\"someArg\");someArray[3];"), "someFunc(\\\"someArg\\\");someArray[3];");
52+
53+
// keep quotes
54+
EXPECT_EQ(test.Decode("\"\"", kFALSE), "\"\"");
55+
56+
// keep quotes and escape inside quotes
57+
EXPECT_EQ(test.Decode("\"\"\"", kFALSE), "\"\\\"\"");
58+
59+
// keep quotes and keep german letters - remove new line
60+
EXPECT_EQ(test.Decode("\"Gänse\nfüßchen\"", kFALSE), "\"Gänsefüßchen\"");
61+
}
62+
63+
// check JSON representation for the objects
64+
TEST(TRootSniffer, root_json)
65+
{
66+
TNamed obj("obj", "title");
67+
68+
TRootSnifferFull sniffer("sniffer");
69+
70+
sniffer.RegisterObject("/", &obj);
71+
72+
std::string res;
73+
sniffer.Produce("/obj", "root.json", "", res);
74+
EXPECT_EQ(res, "{\n"
75+
" \"_typename\" : \"TNamed\",\n"
76+
" \"fUniqueID\" : 0,\n"
77+
" \"fBits\" : 8,\n"
78+
" \"fName\" : \"obj\",\n"
79+
" \"fTitle\" : \"title\"\n"
80+
"}");
81+
}
82+
83+
// check XML representation for the objects
84+
TEST(TRootSniffer, root_xml)
85+
{
86+
TNamed obj("obj", "title");
87+
88+
TRootSnifferFull sniffer("sniffer");
89+
90+
sniffer.RegisterObject("/", &obj);
91+
92+
std::string res;
93+
sniffer.Produce("/obj", "root.xml", "", res);
94+
EXPECT_EQ(res, "<Object class=\"TNamed\">\n"
95+
" <TNamed version=\"1\">\n"
96+
" <TObject fUniqueID=\"0\" fBits=\"8\"/>\n"
97+
" <fName str=\"obj\"/>\n"
98+
" <fTitle str=\"title\"/>\n"
99+
" </TNamed>\n"
100+
"</Object>\n");
101+
}
102+
103+
// check BINARY representation for the objects
104+
TEST(TRootSniffer, root_bin)
105+
{
106+
TNamed obj("obj", "title");
107+
108+
TRootSnifferFull sniffer("sniffer");
109+
110+
sniffer.RegisterObject("/", &obj);
111+
112+
std::string res;
113+
sniffer.Produce("/obj", "root.bin", "", res);
114+
// keep minimal margin for binary format change
115+
EXPECT_NEAR(res.length(), 26, 4);
116+
}
117+
118+
// check root file creation for the objects
119+
TEST(TRootSniffer, file_root)
120+
{
121+
TNamed obj("obj", "title");
122+
123+
TRootSnifferFull sniffer("sniffer");
124+
125+
sniffer.RegisterObject("/", &obj);
126+
127+
std::string res;
128+
sniffer.Produce("/obj", "file.root", "", res);
129+
EXPECT_NEAR(res.length(), 2097152, 10000) << "size of file.root request";
130+
}
131+
132+
// check hierarchy request
133+
TEST(TRootSniffer, item_json)
134+
{
135+
TNamed obj("obj", "title");
136+
137+
TRootSnifferFull sniffer("sniffer");
138+
139+
sniffer.RegisterObject("/", &obj);
140+
141+
std::string res;
142+
sniffer.Produce("/obj", "item.json", "", res);
143+
144+
EXPECT_EQ(res, "{\n"
145+
" \"_name\" : \"obj\",\n"
146+
" \"_root_version\" : " + std::to_string(gROOT->GetVersionCode()) + ",\n"
147+
" \"_kind\" : \"ROOT.TNamed\",\n"
148+
" \"_title\" : \"title\"\n"
149+
"}") << "return value of item.json";
150+
}
151+
152+
// simple method execution
153+
TEST(TRootSniffer, exe_json)
154+
{
155+
TNamed obj("obj","title");
156+
157+
TRootSnifferFull sniffer("sniffer");
158+
159+
sniffer.RegisterObject("/", &obj);
160+
161+
std::string res0;
162+
// by default methods execution is not allowed
163+
sniffer.Produce("/obj", "exe.json", "method=GetTitle", res0);
164+
EXPECT_EQ(res0, "") << "return value of exe.json in readonly";
165+
166+
// disable readonly to get method executed
167+
sniffer.SetReadOnly(kFALSE);
168+
169+
// only now one can execute method
170+
std::string res1;
171+
sniffer.Produce("/obj", "exe.json", "method=GetTitle", res1);
172+
EXPECT_EQ(res1, "\"title\"") << "return value of exe.json for GetTitle";
173+
}
174+
175+
176+
// execute method with post data - lot of gymnastic around
177+
TEST(TRootSniffer, exe_post_json)
178+
{
179+
TH1I hist("hist", "title", 10, 0, 10);
180+
hist.SetDirectory(nullptr);
181+
hist.SetBinContent(5, 10);
182+
183+
std::string json;
184+
{
185+
// only temporary to create json
186+
TH1I hist2("hist", "title", 10, 0, 10);
187+
hist.SetDirectory(nullptr);
188+
hist2.SetBinContent(5, 20);
189+
json = TBufferJSON::ToJSON(&hist2).Data();
190+
}
191+
192+
TRootSnifferFull sniffer("sniffer");
193+
194+
sniffer.RegisterObject("/", &hist);
195+
196+
// disable readonly to execute method
197+
sniffer.SetReadOnly(kFALSE);
198+
// allow use of POST data to decode object from JSON
199+
sniffer.SetAllowPostObject(kTRUE);
200+
201+
THttpCallArg arg;
202+
arg.SetPostData(std::move(json));
203+
sniffer.SetCurrentCallArg(&arg);
204+
205+
// before execution content is 10
206+
EXPECT_EQ(hist.GetBinContent(5), 10);
207+
208+
std::string res;
209+
sniffer.Produce("/hist", "exe.json", "method=Add&prototype='const TH1*,Double_t'&h1=_post_object_json_&_destroy_post_", res);
210+
EXPECT_EQ(res, "1") << "return value of exe.json";
211+
212+
// and now most important - bin content has to change
213+
EXPECT_EQ(hist.GetBinContent(5), 30) << "check of histogram content";
214+
}
215+
216+
// changing object title
217+
TEST(TRootSniffer, set_title)
218+
{
219+
TNamed obj("obj", "title");
220+
221+
TRootSnifferFull sniffer("sniffer");
222+
// disable readonly to get method executed
223+
sniffer.SetReadOnly(kFALSE);
224+
225+
sniffer.RegisterObject("/", &obj);
226+
227+
std::string res;
228+
229+
sniffer.Produce("/obj", "exe.json", "method=SetTitle&title=NewTitle", res);
230+
EXPECT_EQ(res, "null") << "return value of exe.json when methout return void";
231+
EXPECT_EQ(std::string("NewTitle"), obj.GetTitle()) << "compare object title with applied value";
232+
233+
res = "";
234+
sniffer.Produce("/obj", "exe.json", "method=SetTitle&title=\"QuotedTitle\"", res);
235+
EXPECT_EQ(res, "null");
236+
EXPECT_EQ(std::string("QuotedTitle"), obj.GetTitle()) << "compare object title with applied value";
237+
238+
res = "";
239+
sniffer.Produce("/obj", "exe.json", "method=SetTitle&title=%22UrlStyleQuotedTitle%22", res);
240+
EXPECT_EQ(res, "null");
241+
EXPECT_EQ(std::string("UrlStyleQuotedTitle"), obj.GetTitle()) << "compare object title with applied value";
242+
243+
res = "";
244+
sniffer.Produce("/obj", "exe.json", "method=SetTitle&title=Mail\"Formed\"Title", res);
245+
EXPECT_EQ(res, "null");
246+
EXPECT_EQ(std::string("Mail\"Formed\"Title"), obj.GetTitle()) << "compare object title with applied value";
247+
}
248+
249+
// testing command execution with different signatures
250+
TEST(TRootSniffer, cmd_json)
251+
{
252+
TNamed obj("obj", "title");
253+
254+
TRootSnifferFull sniffer("sniffer");
255+
sniffer.SetReadOnly(kFALSE);
256+
257+
sniffer.RegisterObject("/", &obj);
258+
sniffer.RegisterCommand("/Print1", "/obj/->Print(%arg1%)", "");
259+
sniffer.RegisterCommand("/Print2", "/obj/->Print(\"%arg1%\")", "");
260+
sniffer.RegisterCommand("/GetSize", "/obj/->Sizeof()", "");
261+
262+
std::string res;
263+
// quotes are in URL
264+
sniffer.Produce("/Print1", "cmd.json", "arg1=%22*%22", res);
265+
EXPECT_EQ(res, "0") << "return value of cmd.json";
266+
267+
res = "";
268+
// skipping quotes from URL - when they are necessary
269+
// sniffer should have add them automatically
270+
sniffer.Produce("/Print1", "cmd.json", "arg1=*", res);
271+
EXPECT_EQ(res, "0") << "return value of cmd.json";
272+
273+
res = "";
274+
// skipping quotes from URL - when they are necessary
275+
// while value looks like number, sniffer will not quote it
276+
// result of process line is not result is
277+
sniffer.Produce("/Print1", "cmd.json", "arg1=0", res);
278+
EXPECT_EQ(res, "0") << "return value of cmd.json";
279+
280+
res = "";
281+
// quotes are in command definition
282+
sniffer.Produce("/Print2", "cmd.json", "arg1=*", res);
283+
EXPECT_EQ(res, "0") << "return value of cmd.json";
284+
285+
res = "";
286+
// quotes are in command definition but we try to add our own
287+
// sniffer will remove them
288+
sniffer.Produce("/Print2", "cmd.json", "arg1=\"*\"", res);
289+
EXPECT_EQ(res, "0") << "return value of cmd.json";
290+
291+
res = "";
292+
// Execute command which returns some value
293+
sniffer.Produce("/GetSize", "cmd.json", "", res);
294+
// returns only strings sizes
295+
EXPECT_EQ(res, "10") << "return value of cmd.json with object size";
296+
}
297+
298+
// check JSON representation for the objects
299+
TEST(TRootSniffer, multi_json)
300+
{
301+
TNamed obj1("obj1", "title1");
302+
TNamed obj2("obj2", "title2");
303+
304+
TRootSnifferFull sniffer("sniffer");
305+
306+
sniffer.RegisterObject("/", &obj1);
307+
sniffer.RegisterObject("/", &obj2);
308+
309+
std::string items = "/obj1/root.json\n/obj2/root.json\n";
310+
311+
THttpCallArg arg;
312+
arg.SetPostData(std::move(items));
313+
sniffer.SetCurrentCallArg(&arg);
314+
315+
std::string res;
316+
sniffer.Produce("", "multi.json", "number=2", res);
317+
EXPECT_EQ(res, "[{\n"
318+
" \"_typename\" : \"TNamed\",\n"
319+
" \"fUniqueID\" : 0,\n"
320+
" \"fBits\" : 8,\n"
321+
" \"fName\" : \"obj1\",\n"
322+
" \"fTitle\" : \"title1\"\n"
323+
"}, {\n"
324+
" \"_typename\" : \"TNamed\",\n"
325+
" \"fUniqueID\" : 0,\n"
326+
" \"fBits\" : 8,\n"
327+
" \"fName\" : \"obj2\",\n"
328+
" \"fTitle\" : \"title2\"\n"
329+
"}]") << "return value of multi.json";
330+
}

0 commit comments

Comments
 (0)