xref: /aosp_15_r20/system/linkerconfig/contents/tests/configuration/include/linkerconfigparser.h (revision e5eeaa8e05bc25a862c0c861bda7c8a6bfb42dad)
1*e5eeaa8eSAndroid Build Coastguard Worker /*
2*e5eeaa8eSAndroid Build Coastguard Worker  * Copyright (C) 2019 The Android Open Source Project
3*e5eeaa8eSAndroid Build Coastguard Worker  *
4*e5eeaa8eSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*e5eeaa8eSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*e5eeaa8eSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*e5eeaa8eSAndroid Build Coastguard Worker  *
8*e5eeaa8eSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*e5eeaa8eSAndroid Build Coastguard Worker  *
10*e5eeaa8eSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*e5eeaa8eSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*e5eeaa8eSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*e5eeaa8eSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*e5eeaa8eSAndroid Build Coastguard Worker  * limitations under the License.
15*e5eeaa8eSAndroid Build Coastguard Worker  */
16*e5eeaa8eSAndroid Build Coastguard Worker #pragma once
17*e5eeaa8eSAndroid Build Coastguard Worker 
18*e5eeaa8eSAndroid Build Coastguard Worker #include <cstring>
19*e5eeaa8eSAndroid Build Coastguard Worker #include <regex>
20*e5eeaa8eSAndroid Build Coastguard Worker 
21*e5eeaa8eSAndroid Build Coastguard Worker #include "gtest/gtest.h"
22*e5eeaa8eSAndroid Build Coastguard Worker #include "modules.h"
23*e5eeaa8eSAndroid Build Coastguard Worker 
24*e5eeaa8eSAndroid Build Coastguard Worker namespace {
25*e5eeaa8eSAndroid Build Coastguard Worker constexpr const char* kSectionNameRegex = "\\[\\s*([\\w.]+)\\s*\\]";
26*e5eeaa8eSAndroid Build Coastguard Worker constexpr const char* kDirRegex = "dir\\.([\\w.]+)\\s*=\\s*([\\w_.\\-/]+)";
27*e5eeaa8eSAndroid Build Coastguard Worker constexpr const char* kNamespaceBaseRegex =
28*e5eeaa8eSAndroid Build Coastguard Worker     "namespace\\.(\\w+)\\.([^\\s=]+)\\s*(=|\\+=)\\s*([^\\s]+)";
29*e5eeaa8eSAndroid Build Coastguard Worker constexpr const char* kAdditionalNamespacesRegex =
30*e5eeaa8eSAndroid Build Coastguard Worker     "additional\\.namespaces\\s*=\\s*((?:[\\w]+)(?:,[\\w]+)*)";
31*e5eeaa8eSAndroid Build Coastguard Worker 
32*e5eeaa8eSAndroid Build Coastguard Worker // Functions to parse configuration string and verify syntax
33*e5eeaa8eSAndroid Build Coastguard Worker 
ParseDirPath(const std::string & line,Configuration & conf)34*e5eeaa8eSAndroid Build Coastguard Worker inline void ParseDirPath(const std::string& line, Configuration& conf) {
35*e5eeaa8eSAndroid Build Coastguard Worker   static std::regex dir_regex(kDirRegex);
36*e5eeaa8eSAndroid Build Coastguard Worker   std::smatch match;
37*e5eeaa8eSAndroid Build Coastguard Worker 
38*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_TRUE(std::regex_match(line, match, dir_regex)) << line;
39*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_EQ(3u, match.size()) << line;
40*e5eeaa8eSAndroid Build Coastguard Worker   std::string section_name = match[1];
41*e5eeaa8eSAndroid Build Coastguard Worker   std::string dir_path = match[2];
42*e5eeaa8eSAndroid Build Coastguard Worker 
43*e5eeaa8eSAndroid Build Coastguard Worker   if (!MapContainsKey(conf.sections, section_name)) {
44*e5eeaa8eSAndroid Build Coastguard Worker     conf.sections[section_name].name = section_name;
45*e5eeaa8eSAndroid Build Coastguard Worker     conf.sections[section_name].namespaces["default"].name = "default";
46*e5eeaa8eSAndroid Build Coastguard Worker   }
47*e5eeaa8eSAndroid Build Coastguard Worker 
48*e5eeaa8eSAndroid Build Coastguard Worker   conf.sections[section_name].dirs.push_back(dir_path);
49*e5eeaa8eSAndroid Build Coastguard Worker }
50*e5eeaa8eSAndroid Build Coastguard Worker 
ParseAdditionalNamespaces(const std::smatch & match,Section & current_section)51*e5eeaa8eSAndroid Build Coastguard Worker inline void ParseAdditionalNamespaces(const std::smatch& match,
52*e5eeaa8eSAndroid Build Coastguard Worker                                       Section& current_section) {
53*e5eeaa8eSAndroid Build Coastguard Worker   // additional.namespace = a,b,c,e,d
54*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_EQ(2u, match.size());
55*e5eeaa8eSAndroid Build Coastguard Worker   std::stringstream namespaces(match[1]);
56*e5eeaa8eSAndroid Build Coastguard Worker   for (std::string namespace_name;
57*e5eeaa8eSAndroid Build Coastguard Worker        std::getline(namespaces, namespace_name, ',');) {
58*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_FALSE(MapContainsKey(current_section.namespaces, namespace_name))
59*e5eeaa8eSAndroid Build Coastguard Worker         << "Namespace " << namespace_name << " already exists";
60*e5eeaa8eSAndroid Build Coastguard Worker     Namespace new_namespace;
61*e5eeaa8eSAndroid Build Coastguard Worker     new_namespace.name = namespace_name;
62*e5eeaa8eSAndroid Build Coastguard Worker     current_section.namespaces[namespace_name] = new_namespace;
63*e5eeaa8eSAndroid Build Coastguard Worker   }
64*e5eeaa8eSAndroid Build Coastguard Worker }
65*e5eeaa8eSAndroid Build Coastguard Worker 
ParseNamespacePath(const std::vector<std::string> & property_descs,const bool is_additional,const std::string & path,Namespace & current_namespace,const std::string & line)66*e5eeaa8eSAndroid Build Coastguard Worker inline void ParseNamespacePath(const std::vector<std::string>& property_descs,
67*e5eeaa8eSAndroid Build Coastguard Worker                                const bool is_additional, const std::string& path,
68*e5eeaa8eSAndroid Build Coastguard Worker                                Namespace& current_namespace,
69*e5eeaa8eSAndroid Build Coastguard Worker                                const std::string& line) {
70*e5eeaa8eSAndroid Build Coastguard Worker   // namespace.test.(asan.)search|permitted.path =|+= /path/to/${LIB}/dir
71*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_EQ(
72*e5eeaa8eSAndroid Build Coastguard Worker       property_descs[0] == "asan" || property_descs[0] == "hwasan" ? 3u : 2u,
73*e5eeaa8eSAndroid Build Coastguard Worker       property_descs.size());
74*e5eeaa8eSAndroid Build Coastguard Worker 
75*e5eeaa8eSAndroid Build Coastguard Worker   std::vector<std::string>* target_path = nullptr;
76*e5eeaa8eSAndroid Build Coastguard Worker   if (property_descs[0] == "search") {
77*e5eeaa8eSAndroid Build Coastguard Worker     target_path = &current_namespace.search_path;
78*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[0] == "permitted") {
79*e5eeaa8eSAndroid Build Coastguard Worker     target_path = &current_namespace.permitted_path;
80*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[0] == "asan" && property_descs[1] == "search") {
81*e5eeaa8eSAndroid Build Coastguard Worker     target_path = &current_namespace.asan_search_path;
82*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[0] == "asan" && property_descs[1] == "permitted") {
83*e5eeaa8eSAndroid Build Coastguard Worker     target_path = &current_namespace.asan_permitted_path;
84*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[0] == "hwasan" && property_descs[1] == "search") {
85*e5eeaa8eSAndroid Build Coastguard Worker     target_path = &current_namespace.hwasan_search_path;
86*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[0] == "hwasan" &&
87*e5eeaa8eSAndroid Build Coastguard Worker              property_descs[1] == "permitted") {
88*e5eeaa8eSAndroid Build Coastguard Worker     target_path = &current_namespace.hwasan_permitted_path;
89*e5eeaa8eSAndroid Build Coastguard Worker   }
90*e5eeaa8eSAndroid Build Coastguard Worker 
91*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_NE(nullptr, target_path) << line;
92*e5eeaa8eSAndroid Build Coastguard Worker   EXPECT_EQ(is_additional, target_path->size() != 0)
93*e5eeaa8eSAndroid Build Coastguard Worker       << "Path should be marked as = if and only if it is mentioned first : "
94*e5eeaa8eSAndroid Build Coastguard Worker       << line;
95*e5eeaa8eSAndroid Build Coastguard Worker 
96*e5eeaa8eSAndroid Build Coastguard Worker   target_path->push_back(path);
97*e5eeaa8eSAndroid Build Coastguard Worker }
98*e5eeaa8eSAndroid Build Coastguard Worker 
ParseLinkList(const std::vector<std::string> & property_descs,const std::string & target_namespaces,Namespace & current_namespace,Section & current_section,const std::string & line)99*e5eeaa8eSAndroid Build Coastguard Worker inline void ParseLinkList(const std::vector<std::string>& property_descs,
100*e5eeaa8eSAndroid Build Coastguard Worker                           const std::string& target_namespaces,
101*e5eeaa8eSAndroid Build Coastguard Worker                           Namespace& current_namespace,
102*e5eeaa8eSAndroid Build Coastguard Worker                           Section& current_section, const std::string& line) {
103*e5eeaa8eSAndroid Build Coastguard Worker   // namespace.test.links = a,b,c,d,e
104*e5eeaa8eSAndroid Build Coastguard Worker   EXPECT_EQ(1u, property_descs.size());
105*e5eeaa8eSAndroid Build Coastguard Worker   std::stringstream namespaces(target_namespaces);
106*e5eeaa8eSAndroid Build Coastguard Worker   for (std::string namespace_to; std::getline(namespaces, namespace_to, ',');) {
107*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_FALSE(MapContainsKey(current_namespace.links, namespace_to))
108*e5eeaa8eSAndroid Build Coastguard Worker         << "Link to " << namespace_to << " is already defined : " << line;
109*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_TRUE(MapContainsKey(current_section.namespaces, namespace_to))
110*e5eeaa8eSAndroid Build Coastguard Worker         << "Target namespace " << namespace_to
111*e5eeaa8eSAndroid Build Coastguard Worker         << " is not defined in section : " << line;
112*e5eeaa8eSAndroid Build Coastguard Worker 
113*e5eeaa8eSAndroid Build Coastguard Worker     current_namespace.links[namespace_to].from = &current_namespace;
114*e5eeaa8eSAndroid Build Coastguard Worker     current_namespace.links[namespace_to].to =
115*e5eeaa8eSAndroid Build Coastguard Worker         &current_section.namespaces[namespace_to];
116*e5eeaa8eSAndroid Build Coastguard Worker     current_namespace.links[namespace_to].allow_all_shared = false;
117*e5eeaa8eSAndroid Build Coastguard Worker   }
118*e5eeaa8eSAndroid Build Coastguard Worker }
119*e5eeaa8eSAndroid Build Coastguard Worker 
ParseLink(const std::vector<std::string> & property_descs,const bool is_additional,const std::string & value,Namespace & current_namespace,Section & current_section,const std::string & line)120*e5eeaa8eSAndroid Build Coastguard Worker inline void ParseLink(const std::vector<std::string>& property_descs,
121*e5eeaa8eSAndroid Build Coastguard Worker                       const bool is_additional, const std::string& value,
122*e5eeaa8eSAndroid Build Coastguard Worker                       Namespace& current_namespace, Section& current_section,
123*e5eeaa8eSAndroid Build Coastguard Worker                       const std::string& line) {
124*e5eeaa8eSAndroid Build Coastguard Worker   // namespace.from.link.to.shared_libs = a.so
125*e5eeaa8eSAndroid Build Coastguard Worker   // namespace.from.link.to.allow_all_shared_libs = true
126*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_EQ(3u, property_descs.size()) << line;
127*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_TRUE(property_descs[2] == "shared_libs" ||
128*e5eeaa8eSAndroid Build Coastguard Worker               property_descs[2] == "allow_all_shared_libs")
129*e5eeaa8eSAndroid Build Coastguard Worker       << line;
130*e5eeaa8eSAndroid Build Coastguard Worker   std::string namespace_to = property_descs[1];
131*e5eeaa8eSAndroid Build Coastguard Worker 
132*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_TRUE(MapContainsKey(current_section.namespaces, namespace_to))
133*e5eeaa8eSAndroid Build Coastguard Worker       << "To namespace does not exist in section " << current_section.name
134*e5eeaa8eSAndroid Build Coastguard Worker       << " : " << line;
135*e5eeaa8eSAndroid Build Coastguard Worker 
136*e5eeaa8eSAndroid Build Coastguard Worker   if (property_descs[2] == "shared_libs") {
137*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_EQ(is_additional,
138*e5eeaa8eSAndroid Build Coastguard Worker               current_namespace.links[namespace_to].shared_libs.size() != 0)
139*e5eeaa8eSAndroid Build Coastguard Worker         << "Link should be defined with = if and only if it is first link "
140*e5eeaa8eSAndroid Build Coastguard Worker            "between two namespaces : "
141*e5eeaa8eSAndroid Build Coastguard Worker         << line;
142*e5eeaa8eSAndroid Build Coastguard Worker 
143*e5eeaa8eSAndroid Build Coastguard Worker     current_namespace.links[namespace_to].shared_libs.push_back(value);
144*e5eeaa8eSAndroid Build Coastguard Worker   } else {
145*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_EQ("true", value) << line;
146*e5eeaa8eSAndroid Build Coastguard Worker     current_namespace.links[namespace_to].allow_all_shared = true;
147*e5eeaa8eSAndroid Build Coastguard Worker   }
148*e5eeaa8eSAndroid Build Coastguard Worker }
149*e5eeaa8eSAndroid Build Coastguard Worker 
ParseNamespaceCommand(const std::string & namespace_name,const std::string & property_desc,const bool is_additional_property,const std::string & value,Section & current_section,const std::string & line)150*e5eeaa8eSAndroid Build Coastguard Worker inline void ParseNamespaceCommand(const std::string& namespace_name,
151*e5eeaa8eSAndroid Build Coastguard Worker                                   const std::string& property_desc,
152*e5eeaa8eSAndroid Build Coastguard Worker                                   const bool is_additional_property,
153*e5eeaa8eSAndroid Build Coastguard Worker                                   const std::string& value,
154*e5eeaa8eSAndroid Build Coastguard Worker                                   Section& current_section,
155*e5eeaa8eSAndroid Build Coastguard Worker                                   const std::string& line) {
156*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_TRUE(MapContainsKey(current_section.namespaces, namespace_name))
157*e5eeaa8eSAndroid Build Coastguard Worker       << "Namespace " << namespace_name << " does not exist in section "
158*e5eeaa8eSAndroid Build Coastguard Worker       << current_section.name << " : " << line;
159*e5eeaa8eSAndroid Build Coastguard Worker   Namespace& current_namespace = current_section.namespaces[namespace_name];
160*e5eeaa8eSAndroid Build Coastguard Worker 
161*e5eeaa8eSAndroid Build Coastguard Worker   std::vector<std::string> property_descs;
162*e5eeaa8eSAndroid Build Coastguard Worker   std::stringstream property_desc_stream(property_desc);
163*e5eeaa8eSAndroid Build Coastguard Worker   for (std::string property;
164*e5eeaa8eSAndroid Build Coastguard Worker        std::getline(property_desc_stream, property, '.');) {
165*e5eeaa8eSAndroid Build Coastguard Worker     property_descs.push_back(property);
166*e5eeaa8eSAndroid Build Coastguard Worker   }
167*e5eeaa8eSAndroid Build Coastguard Worker 
168*e5eeaa8eSAndroid Build Coastguard Worker   ASSERT_TRUE(property_descs.size() > 0)
169*e5eeaa8eSAndroid Build Coastguard Worker       << "There should be at least one property description after namespace."
170*e5eeaa8eSAndroid Build Coastguard Worker       << namespace_name << " : " << line;
171*e5eeaa8eSAndroid Build Coastguard Worker 
172*e5eeaa8eSAndroid Build Coastguard Worker   if (property_descs[0].compare("isolated") == 0) {
173*e5eeaa8eSAndroid Build Coastguard Worker     // namespace.test.isolated = true
174*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_EQ(1u, property_descs.size()) << line;
175*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_TRUE(value == "true" || value == "false") << line;
176*e5eeaa8eSAndroid Build Coastguard Worker     current_namespace.is_isolated = value == "true";
177*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[0].compare("visible") == 0) {
178*e5eeaa8eSAndroid Build Coastguard Worker     // namespace.test.visible = true
179*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_EQ(1u, property_descs.size()) << line;
180*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_TRUE(value == "true" || value == "false") << line;
181*e5eeaa8eSAndroid Build Coastguard Worker     current_namespace.is_visible = value == "true";
182*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[property_descs.size() - 1] == "paths") {
183*e5eeaa8eSAndroid Build Coastguard Worker     // namespace.test.search.path += /system/lib
184*e5eeaa8eSAndroid Build Coastguard Worker     ParseNamespacePath(
185*e5eeaa8eSAndroid Build Coastguard Worker         property_descs, is_additional_property, value, current_namespace, line);
186*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[0] == "links") {
187*e5eeaa8eSAndroid Build Coastguard Worker     // namespace.test.links = a,b,c
188*e5eeaa8eSAndroid Build Coastguard Worker     ParseLinkList(
189*e5eeaa8eSAndroid Build Coastguard Worker         property_descs, value, current_namespace, current_section, line);
190*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[0] == "link") {
191*e5eeaa8eSAndroid Build Coastguard Worker     // namespace.test.link.a = libc.so
192*e5eeaa8eSAndroid Build Coastguard Worker     ParseLink(property_descs,
193*e5eeaa8eSAndroid Build Coastguard Worker               is_additional_property,
194*e5eeaa8eSAndroid Build Coastguard Worker               value,
195*e5eeaa8eSAndroid Build Coastguard Worker               current_namespace,
196*e5eeaa8eSAndroid Build Coastguard Worker               current_section,
197*e5eeaa8eSAndroid Build Coastguard Worker               line);
198*e5eeaa8eSAndroid Build Coastguard Worker   } else if (property_descs[0] == "allowed_libs") {
199*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_EQ(1u, property_descs.size()) << line;
200*e5eeaa8eSAndroid Build Coastguard Worker     current_namespace.allowed_libs.push_back(value);
201*e5eeaa8eSAndroid Build Coastguard Worker   } else {
202*e5eeaa8eSAndroid Build Coastguard Worker     EXPECT_TRUE(false) << "Failed to parse line : " << line;
203*e5eeaa8eSAndroid Build Coastguard Worker   }
204*e5eeaa8eSAndroid Build Coastguard Worker }
205*e5eeaa8eSAndroid Build Coastguard Worker }  // namespace
206*e5eeaa8eSAndroid Build Coastguard Worker 
ParseConfiguration(const std::string & configuration_str,Configuration & conf)207*e5eeaa8eSAndroid Build Coastguard Worker inline void ParseConfiguration(const std::string& configuration_str,
208*e5eeaa8eSAndroid Build Coastguard Worker                                Configuration& conf) {
209*e5eeaa8eSAndroid Build Coastguard Worker   Section* current_section = nullptr;
210*e5eeaa8eSAndroid Build Coastguard Worker 
211*e5eeaa8eSAndroid Build Coastguard Worker   static std::regex section_name_regex(kSectionNameRegex);
212*e5eeaa8eSAndroid Build Coastguard Worker   static std::regex additional_namespaces_regex(kAdditionalNamespacesRegex);
213*e5eeaa8eSAndroid Build Coastguard Worker   static std::regex namespace_base_regex(kNamespaceBaseRegex);
214*e5eeaa8eSAndroid Build Coastguard Worker 
215*e5eeaa8eSAndroid Build Coastguard Worker   std::smatch match;
216*e5eeaa8eSAndroid Build Coastguard Worker 
217*e5eeaa8eSAndroid Build Coastguard Worker   std::stringstream configuration_stream(configuration_str);
218*e5eeaa8eSAndroid Build Coastguard Worker 
219*e5eeaa8eSAndroid Build Coastguard Worker   for (std::string line; std::getline(configuration_stream, line);) {
220*e5eeaa8eSAndroid Build Coastguard Worker     // Skip empty line
221*e5eeaa8eSAndroid Build Coastguard Worker     if (line.empty()) {
222*e5eeaa8eSAndroid Build Coastguard Worker       continue;
223*e5eeaa8eSAndroid Build Coastguard Worker     }
224*e5eeaa8eSAndroid Build Coastguard Worker 
225*e5eeaa8eSAndroid Build Coastguard Worker     if (std::regex_match(line, match, section_name_regex)) {
226*e5eeaa8eSAndroid Build Coastguard Worker       // [section_name]
227*e5eeaa8eSAndroid Build Coastguard Worker       ASSERT_EQ(2u, match.size()) << line;
228*e5eeaa8eSAndroid Build Coastguard Worker       std::string section_name = match[1];
229*e5eeaa8eSAndroid Build Coastguard Worker       ASSERT_TRUE(MapContainsKey(conf.sections, section_name)) << line;
230*e5eeaa8eSAndroid Build Coastguard Worker       current_section = &conf.sections[section_name];
231*e5eeaa8eSAndroid Build Coastguard Worker 
232*e5eeaa8eSAndroid Build Coastguard Worker       continue;
233*e5eeaa8eSAndroid Build Coastguard Worker     }
234*e5eeaa8eSAndroid Build Coastguard Worker 
235*e5eeaa8eSAndroid Build Coastguard Worker     if (current_section == nullptr) {
236*e5eeaa8eSAndroid Build Coastguard Worker       ParseDirPath(line, conf);
237*e5eeaa8eSAndroid Build Coastguard Worker     } else {
238*e5eeaa8eSAndroid Build Coastguard Worker       if (std::regex_match(line, match, additional_namespaces_regex)) {
239*e5eeaa8eSAndroid Build Coastguard Worker         ParseAdditionalNamespaces(match, *current_section);
240*e5eeaa8eSAndroid Build Coastguard Worker       } else {
241*e5eeaa8eSAndroid Build Coastguard Worker         EXPECT_TRUE(std::regex_match(line, match, namespace_base_regex)) << line;
242*e5eeaa8eSAndroid Build Coastguard Worker         ASSERT_EQ(5u, match.size()) << line;
243*e5eeaa8eSAndroid Build Coastguard Worker         std::string namespace_name = match[1];
244*e5eeaa8eSAndroid Build Coastguard Worker         std::string property_desc = match[2];
245*e5eeaa8eSAndroid Build Coastguard Worker         bool is_additional_property = match[3] == "+=";
246*e5eeaa8eSAndroid Build Coastguard Worker         std::string content = match[4];
247*e5eeaa8eSAndroid Build Coastguard Worker         ParseNamespaceCommand(namespace_name,
248*e5eeaa8eSAndroid Build Coastguard Worker                               property_desc,
249*e5eeaa8eSAndroid Build Coastguard Worker                               is_additional_property,
250*e5eeaa8eSAndroid Build Coastguard Worker                               content,
251*e5eeaa8eSAndroid Build Coastguard Worker                               *current_section,
252*e5eeaa8eSAndroid Build Coastguard Worker                               line);
253*e5eeaa8eSAndroid Build Coastguard Worker       }
254*e5eeaa8eSAndroid Build Coastguard Worker     }
255*e5eeaa8eSAndroid Build Coastguard Worker   }
256*e5eeaa8eSAndroid Build Coastguard Worker }
257