Skip to content

Commit ab58efa

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 - exe.json - exe.json with POST data - item.json - cmd.json - multi.json Also verify basic functionality of TRootSniffer::DecodeUrlOptionValue method
1 parent 772aa0f commit ab58efa

3 files changed

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

0 commit comments

Comments
 (0)