xref: /6.0.3/subjson/tests/t_path.cc (revision 80a9543e)
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
122TEST_F(PathTests, testNegativePath) {
123    Path ss;
124    string pth;
125
126    pth = "foo[-1][-1][-1]";
127    ASSERT_EQ(0, ss.parse(pth));
128    ASSERT_EQ(5, ss.size());
129    ASSERT_TRUE(!!ss.components_s[2].is_neg);
130    ASSERT_TRUE(!!ss.components_s[3].is_neg);
131    ASSERT_TRUE(!!ss.components_s[4].is_neg);
132    ASSERT_TRUE(!!ss.has_negix);
133
134    ss.clear();
135    pth = "foo[-2]";
136    ASSERT_NE(0, ss.parse(pth));
137}
138
139TEST_F(PathTests, testInvalidSequence) {
140    Path ss;
141    string pth;
142
143    #if 0 /* TODO */
144    pth = "[1].[2].[3]";
145    ASSERT_NE(0, ss.parse(pth));
146    #endif
147
148    pth = "hello[0]world";
149    ASSERT_NE(0, ss.parse(pth));
150
151    pth = "[not][a][number]";
152    ASSERT_NE(0, ss.parse(pth));
153}
154
155TEST_F(PathTests, testJsonEscapes) {
156    Path ss;
157    string pth;
158
159    // Reject any path not considered valid as a JSON string. N1QL escapes
160    // are *only* for n1ql syntax tokens (like ., ], [, `)
161    pth = "\"invalid.path";
162    ASSERT_NE(0, ss.parse(pth));
163
164    pth = "\\" "\"quoted.path";
165    ASSERT_EQ(0, ss.parse(pth));
166}
167
168TEST_F(PathTests, testUtf8Path) {
169    Path ss;
170    // \xc3\xba = ú
171    string pth("F\xC3\xBAtbol");
172    ASSERT_EQ(0, ss.parse(pth));
173    ASSERT_EQ("F\xC3\xBAtbol", getComponentString(ss, 1));
174}
175
176TEST_F(PathTests, testGetRootType) {
177    ASSERT_EQ(JSONSL_T_LIST, Util::get_root_type(Command::ARRAY_PREPEND, ""));
178    ASSERT_EQ(JSONSL_T_OBJECT, Util::get_root_type(Command::ARRAY_PREPEND, "a"));
179    ASSERT_EQ(JSONSL_T_UNKNOWN, Util::get_root_type(Command::DICT_UPSERT, ""));
180    ASSERT_EQ(JSONSL_T_LIST, Util::get_root_type(Command::ARRAY_APPEND_P, ""));
181    ASSERT_EQ(JSONSL_T_LIST, Util::get_root_type(Command::ARRAY_APPEND, "[1]"));
182    ASSERT_EQ(JSONSL_T_LIST, Util::get_root_type(Command::ARRAY_PREPEND, "[-1]"));
183}
184