xref: /6.0.3/subjson/tests/t_path.cc (revision c30c3d4c)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3*     Copyright 2015 Couchbase, Inc
4*
5*   Licensed under the Apache License, Version 2.0 (the "License");
6*   you may not use this file except in compliance with the License.
7*   You may obtain a copy of the License at
8*
9*       http://www.apache.org/licenses/LICENSE-2.0
10*
11*   Unless required by applicable law or agreed to in writing, software
12*   distributed under the License is distributed on an "AS IS" BASIS,
13*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*   See the License for the specific language governing permissions and
15*   limitations under the License.
16*/
17#include "subdoc-tests-common.h"
18
19using std::string;
20using std::cerr;
21using std::endl;
22using Subdoc::Path;
23using Subdoc::Util;
24using Subdoc::Command;
25
26class PathTests : public ::testing::Test {};
27
28static void
29getComponentString(const Path& nj, int ix, string& out) {
30    const auto& comp = nj.get_component(ix);
31    out.assign(comp.pstr, comp.len);
32}
33static string
34getComponentString(const Path& nj, int ix) {
35    string tmp;
36    getComponentString(nj, ix, tmp);
37    return tmp;
38}
39static unsigned long
40getComponentNumber(const Path& nj, int ix) {
41    return nj.get_component(ix).idx;
42}
43
44TEST_F(PathTests, testBasic)
45{
46    Path ss;
47
48    string pth1("foo.bar.baz");
49    ASSERT_EQ(0, ss.parse(pth1));
50    ASSERT_EQ(4, ss.size());
51
52    ASSERT_EQ(JSONSL_PATH_ROOT, ss[0].ptype);
53    ASSERT_EQ(JSONSL_PATH_STRING, ss[1].ptype);
54    ASSERT_EQ(JSONSL_PATH_STRING, ss[2].ptype);
55    ASSERT_EQ(JSONSL_PATH_STRING, ss[3].ptype);
56
57
58    ASSERT_EQ("foo", getComponentString(ss, 1));
59    ASSERT_EQ("bar", getComponentString(ss, 2));
60    ASSERT_EQ("baz", getComponentString(ss, 3));
61
62    ss.clear();
63    pth1 = "....";
64    ASSERT_NE(0, ss.parse(pth1));
65}
66
67TEST_F(PathTests, testNumericIndices) {
68    Path ss;
69    string pth = "array[1].item[9]";
70
71    ASSERT_EQ(0, ss.parse(pth));
72    ASSERT_EQ(5, ss.size());
73
74    ASSERT_EQ(JSONSL_PATH_STRING, ss[1].ptype);
75    ASSERT_EQ("array", getComponentString(ss, 1));
76
77    ASSERT_EQ(JSONSL_PATH_NUMERIC, ss[2].ptype);
78    ASSERT_EQ(1, getComponentNumber(ss, 2));
79
80    ASSERT_EQ(JSONSL_PATH_STRING, ss[3].ptype);
81    ASSERT_EQ("item", getComponentString(ss, 3));
82
83    ASSERT_EQ(JSONSL_PATH_NUMERIC, ss[4].ptype);
84    ASSERT_EQ(9, getComponentNumber(ss, 4));
85
86    // Try again, using [] syntax
87    pth = "foo[0][0][0]";
88    ASSERT_EQ(0, ss.parse(pth));
89    ASSERT_EQ(5, ss.size());
90
91    pth = "[1][2][3]";
92    ASSERT_EQ(0, ss.parse(pth));
93    ASSERT_EQ(4, ss.size());
94}
95
96TEST_F(PathTests, testEscapes)
97{
98    Path ss;
99    string pth;
100
101    pth = "`simple`.`escaped`.`path`";
102    ASSERT_EQ(0, ss.parse(pth));
103    ASSERT_EQ("simple", getComponentString(ss, 1));
104    ASSERT_EQ("escaped", getComponentString(ss, 2));
105    ASSERT_EQ("path", getComponentString(ss, 3));
106
107    // Try something more complex
108    ss.clear();
109    pth = "escaped.`arr.idx`[9]";
110    ASSERT_EQ(0, ss.parse(pth));
111    ASSERT_EQ("escaped", getComponentString(ss, 1));
112    ASSERT_EQ("arr.idx", getComponentString(ss, 2));
113    ASSERT_EQ(9, getComponentNumber(ss, 3));
114
115    ss.clear();
116    pth = "`BACKTICK``HAPPY`.`CAMPER`";
117    ASSERT_EQ(0, ss.parse(pth));
118    ASSERT_EQ("BACKTICK`HAPPY", getComponentString(ss, 1));
119    ASSERT_EQ("CAMPER", getComponentString(ss, 2));
120
121    // MB-30278: A subsequent parse of a path containing an escaped symbol
122    // fails due to incorrect caching.
123    ss.clear();
124    pth = "trailing``";
125    ASSERT_EQ(0, ss.parse(pth));
126    ASSERT_EQ("trailing`", getComponentString(ss, 1));
127}
128
129TEST_F(PathTests, testNegativePath) {
130    Path ss;
131    string pth;
132
133    pth = "foo[-1][-1][-1]";
134    ASSERT_EQ(0, ss.parse(pth));
135    ASSERT_EQ(5, ss.size());
136    ASSERT_TRUE(!!ss.components_s[2].is_neg);
137    ASSERT_TRUE(!!ss.components_s[3].is_neg);
138    ASSERT_TRUE(!!ss.components_s[4].is_neg);
139    ASSERT_TRUE(!!ss.has_negix);
140
141    ss.clear();
142    pth = "foo[-2]";
143    ASSERT_NE(0, ss.parse(pth));
144}
145
146TEST_F(PathTests, testInvalidSequence) {
147    Path ss;
148    string pth;
149
150    #if 0 /* TODO */
151    pth = "[1].[2].[3]";
152    ASSERT_NE(0, ss.parse(pth));
153    #endif
154
155    pth = "hello[0]world";
156    ASSERT_NE(0, ss.parse(pth));
157
158    pth = "[not][a][number]";
159    ASSERT_NE(0, ss.parse(pth));
160}
161
162TEST_F(PathTests, testJsonEscapes) {
163    Path ss;
164    string pth;
165
166    // Reject any path not considered valid as a JSON string. N1QL escapes
167    // are *only* for n1ql syntax tokens (like ., ], [, `)
168    pth = "\"invalid.path";
169    ASSERT_NE(0, ss.parse(pth));
170
171    pth = "\\" "\"quoted.path";
172    ASSERT_EQ(0, ss.parse(pth));
173}
174
175TEST_F(PathTests, testUtf8Path) {
176    Path ss;
177    // \xc3\xba = ú
178    string pth("F\xC3\xBAtbol");
179    ASSERT_EQ(0, ss.parse(pth));
180    ASSERT_EQ("F\xC3\xBAtbol", getComponentString(ss, 1));
181}
182
183TEST_F(PathTests, testGetRootType) {
184    ASSERT_EQ(JSONSL_T_LIST, Util::get_root_type(Command::ARRAY_PREPEND, ""));
185    ASSERT_EQ(JSONSL_T_OBJECT, Util::get_root_type(Command::ARRAY_PREPEND, "a"));
186    ASSERT_EQ(JSONSL_T_UNKNOWN, Util::get_root_type(Command::DICT_UPSERT, ""));
187    ASSERT_EQ(JSONSL_T_LIST, Util::get_root_type(Command::ARRAY_APPEND_P, ""));
188    ASSERT_EQ(JSONSL_T_LIST, Util::get_root_type(Command::ARRAY_APPEND, "[1]"));
189    ASSERT_EQ(JSONSL_T_LIST, Util::get_root_type(Command::ARRAY_PREPEND, "[-1]"));
190}
191