Skip to content

Commit 69ba55a

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
1 parent 772aa0f commit 69ba55a

3 files changed

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

0 commit comments

Comments
 (0)