1- % %%-------------------------------------------------------------------
2- % %% File : xpath_abbrev.erl
3- % %% Author : Bertil Karlsson <bertil@finrod>
4- % %% Description :
5- % %%
6- % %% Created : 17 Jan 2006 by Bertil Karlsson <bertil@finrod>
7- % %%-------------------------------------------------------------------
1+ % % %CopyrightBegin%
2+ % %
3+ % % SPDX-License-Identifier: Apache-2.0
4+ % %
5+ % % Copyright Ericsson AB 2026. All Rights Reserved.
6+ % %
7+ % % Licensed under the Apache License, Version 2.0 (the "License");
8+ % % you may not use this file except in compliance with the License.
9+ % % You may obtain a copy of the License at
10+ % %
11+ % % http://www.apache.org/licenses/LICENSE-2.0
12+ % %
13+ % % Unless required by applicable law or agreed to in writing, software
14+ % % distributed under the License is distributed on an "AS IS" BASIS,
15+ % % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+ % % See the License for the specific language governing permissions and
17+ % % limitations under the License.
18+ % %
19+ % % %CopyrightEnd%
20+
821-module (xpath_abbrev ).
922
1023-export ([test /0 , check_node_set /2 , ticket_6873 /0 , ticket_7496 /0 , functions /0 ]).
24+ -export ([relational_operators /0 ]).
1125-export ([namespaces /0 ]).
1226
1327-include_lib (" common_test/include/ct.hrl" ).
@@ -61,7 +75,7 @@ test() ->
6175 Res22 = xmerl_xpath :string (" blipp[@id and @test]" ,E ),
6276 ok = check_node_set (" blipp[@id and @test]" ,Res22 ).
6377
64- check_node_set (" blipp" ,[E1 ,E2 ,E3 ]) ->
78+ check_node_set (" blipp" ,[E1 ,E2 ,E3 ]) ->
6579 ok = xml_element_name (E1 ,blipp ),
6680 ok = xml_element_name (E2 ,blipp ),
6781 ok = xml_element_name (E3 ,blipp ),
@@ -106,7 +120,7 @@ check_node_set("/myBS_model/blipp[3]/blupp[2]",[E]) ->
106120 # xmlElement {name = blupp ,
107121 attributes = [# xmlAttribute {name = att ,value = " bluppc2" }]}= E ,
108122 ok ;
109- check_node_set (" blipp//plopp" ,[# xmlElement {name = plopp },# xmlElement {name = plopp }]) ->
123+ check_node_set (" blipp//plopp" ,[# xmlElement {name = plopp },# xmlElement {name = plopp }]) ->
110124 ok ;
111125check_node_set (" //plopp" ,[E1 ,E2 ,E3 ]) ->
112126 ok = xml_element_name (E1 ,plopp ),
@@ -218,6 +232,33 @@ ticket_7496() ->
218232 ok = Test (Doc3 ," //*[starts-with(local-name(),'p')]" ,
219233 [parent ,pet ,pet ]).
220234
235+ % % Regression test for the relational operators '<', '<=' and '>='.
236+ % % These three clauses of xmerl_xpath_pred:comp_expr/4 each carried the
237+ % % body of '>' (a copy-paste error), so every one evaluated as 'a > b'.
238+ % % The cases below fail on the buggy code (each selects [e,f] / [] instead
239+ % % of the expected node sets) and pass once the operators are correct.
240+ relational_operators () ->
241+ Test = fun (Doc , XPath , Exp ) ->
242+ Result = xmerl_xpath :string (XPath , Doc ),
243+ Exp = [Name || # xmlElement {name = Name } <- Result ],
244+ ok
245+ end ,
246+ {Doc ,_ } = xmerl_scan :string (" <a><b/><c/><d/><e/><f/></a>" ),
247+
248+ % % Relational predicates over position() (number vs number).
249+ ok = Test (Doc , " /a/*[position() < 3]" , [b , c ]),
250+ ok = Test (Doc , " /a/*[position() <= 3]" , [b , c , d ]),
251+ ok = Test (Doc , " /a/*[position() >= 3]" , [d , e , f ]),
252+ ok = Test (Doc , " /a/*[position() > 3]" , [e , f ]),
253+
254+ % % Constant relational predicates, independent of position(): each is a
255+ % % boolean that is true, so it selects every child.
256+ All = [b , c , d , e , f ],
257+ ok = Test (Doc , " /a/*[2 < 3]" , All ),
258+ ok = Test (Doc , " /a/*[3 <= 3]" , All ),
259+ ok = Test (Doc , " /a/*[3 >= 3]" , All ),
260+ ok = Test (Doc , " /a/*[3 > 2]" , All ),
261+ ok .
221262
222263functions () ->
223264 Test = fun (Doc , XPath , Exp ) ->
@@ -234,7 +275,7 @@ functions() ->
234275 end || Obj <- Result ],
235276 ok
236277 end ,
237- Foo =
278+ Foo =
238279 " <foo>"
239280 " <bar>"
240281 " <name>Xml</name>"
@@ -257,6 +298,16 @@ functions() ->
257298 ok = Test (Doc ," /foo/bar[starts-with(name, 'X')]" ,[bar ,bar ]),
258299 ok = Test (Doc ," /foo/bar[value = string(1)]/value/text()" ,[" 1" ]),
259300
301+ % % Regression tests: substring-before used to include the separator's
302+ % % first character (off-by-one); string-length and round returned bare
303+ % % numbers instead of #xmlObj{type=number}; and sum and number() (no
304+ % % args) called string/2 with a bare node, crashing on any non-empty
305+ % % node-set.
306+ ok = Test (Doc ," /foo/bar[substring-before(name, 'a') = 'Xp']" ,[bar ]),
307+ ok = Test (Doc ," /foo/bar[string-length(name) = 3]" ,[bar ]),
308+ ok = Test (Doc ," /foo/bar[round(value) = 2]" ,[bar ]),
309+ ok = Test (Doc ," /foo[sum(bar/value) = 6]" ,[foo ]),
310+ ok = Test (Doc ," /foo/bar/value[number() = 2]" ,[value ]),
260311
261312 {Doc2 ,_ }= xmerl_scan :file (" purchaseOrder.xml" ),
262313 ok = Test (Doc2 ," //*[starts-with(local-name(),'c')]" ,
@@ -266,7 +317,6 @@ functions() ->
266317 ok = Test (Doc2 ," //*[starts-with(name(),'{http://www.example.com/PO1')]" ,
267318 ['apo:purchaseOrder' ,'apo:comment' ]).
268319
269-
270320namespaces () ->
271321 {Doc ,_ } = xmerl_scan :file (" purchaseOrder.xml" , [{namespace_conformant , true }]),
272322
0 commit comments