// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "docfx/doxygen2syntax.h" #include "docfx/testing/inputs.h" #include "docfx/yaml_context.h" #include namespace docfx { namespace { auto constexpr kEnumXml = R"xml( Idempotency google::cloud::Idempotency kIdempotent The operation is idempotent and can be retried after a transient failure. kNonIdempotent The operation is not idempotent and should not be retried after a transient failure. Whether a [truncated]. When a RPC fails with a retryable error [truncated] )xml"; auto constexpr kTypedefXml = R"xml(xml( std::function< std::unique_ptr< BackgroundThreads >()> using google::cloud::BackgroundThreadsFactory = typedef std::function<std::unique_ptr<BackgroundThreads>()> BackgroundThreadsFactory google::cloud::BackgroundThreadsFactory A short description of the thing. A longer description would go here. )xml"; auto constexpr kVariableXml = R"xml(xml( Severity Severity google::cloud::LogRecord::severity severity google::cloud::LogRecord::severity )xml"; auto constexpr kClassXml = R"xml(xml( google::cloud::RuntimeStatusError std::runtime_error google/cloud/status.h Status Status google::cloud::RuntimeStatusError::status_ status_ google::cloud::RuntimeStatusError::status_ google::cloud::RuntimeStatusError::RuntimeStatusError (Status status) RuntimeStatusError google::cloud::RuntimeStatusError::RuntimeStatusError Status status Status const & Status const & google::cloud::RuntimeStatusError::status () const status google::cloud::RuntimeStatusError::status A runtime error that wraps a google::cloud::Status. google::cloud::RuntimeStatusErrorRuntimeStatusError google::cloud::RuntimeStatusErrorstatus google::cloud::RuntimeStatusErrorstatus_ )xml"; auto constexpr kStructXml = R"xml(xml(xml( google::cloud::AsyncTimerResult google/cloud/async_operation.h std::chrono::system_clock::time_point std::chrono::system_clock::time_point google::cloud::AsyncTimerResult::deadline deadline google::cloud::AsyncTimerResult::deadline bool bool google::cloud::AsyncTimerResult::cancelled cancelled google::cloud::AsyncTimerResult::cancelled The result of an async timer operation. Callbacks for async timers will receive an object of this class. google::cloud::AsyncTimerResultcancelled google::cloud::AsyncTimerResultdeadline )xml"; auto constexpr kNamespaceXml = R"xml(xml( google::cloud::mocks Contains helpers for testing the Google Cloud C++ Client Libraries. The symbols defined in this namespace are offered for public consumption. )xml"; TEST(Doxygen2SyntaxContent, Enum) { auto constexpr kExpected = R"""(enum class google::cloud::Idempotency { kIdempotent, kNonIdempotent, };)"""; pugi::xml_document doc; doc.load_string(kEnumXml); auto selected = doc.select_node( "//*[@id='" "namespacegoogle_1_1cloud_1a7d65fd569564712b7cfe652613f30d9c" "']"); ASSERT_TRUE(selected); auto const actual = EnumSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, Typedef) { auto constexpr kExpected = R"""(using google::cloud::BackgroundThreadsFactory = std::function< std::unique_ptr< BackgroundThreads >()>;)"""; pugi::xml_document doc; doc.load_string(kTypedefXml); auto selected = doc.select_node( "//*[@id='" "namespacegoogle_1_1cloud_1a1498c1ea55d81842f37bbc42d003df1f" "']"); ASSERT_TRUE(selected); auto const actual = TypedefSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, FriendStruct) { auto constexpr kXml = R"xml(xml( struct friend struct DoxygenTest1 DoxygenTest1 google::cloud::DoxygenTest2::DoxygenTest1 DoxygenTest1 The BFF friendliest friend ever. )xml"; auto constexpr kExpected = R"""(friend struct google::cloud::DoxygenTest2::DoxygenTest1;)"""; pugi::xml_document doc; doc.load_string(kXml); auto selected = doc.select_node( "//*[@id='" "structgoogle_1_1cloud_1_1DoxygenTest2_1a4ebc3a917ee916646f8af7ce94f83248" "']"); ASSERT_TRUE(selected); auto const actual = FriendSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, FriendFunction) { auto constexpr kXml = R"xml(xml( bool bool operator== (ErrorInfo const &, ErrorInfo const &) operator== google::cloud::ErrorInfo::operator== ErrorInfo const & ErrorInfo const & )xml"; auto constexpr kExpected = R"""(friend bool google::cloud::ErrorInfo::operator== ( ErrorInfo const &, ErrorInfo const & ))"""; pugi::xml_document doc; doc.load_string(kXml); auto selected = doc.select_node( "//*[@id='" "classgoogle_1_1cloud_1_1ErrorInfo_1a3e7a9be9a728e13d3333784f63270555" "']"); ASSERT_TRUE(selected); auto const actual = FriendSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, Variable) { auto constexpr kExpected = R"""(Severity severity;)"""; pugi::xml_document doc; doc.load_string(kVariableXml); auto selected = doc.select_node( "//*[@id='" "structgoogle_1_1cloud_1_1LogRecord_1a830f8e86e1581dddbbb2cd922cbc" "']"); ASSERT_TRUE(selected); auto const actual = VariableSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, Function) { auto constexpr kExpected = R"""(template < typename Rep, typename Period> future< StatusOr< std::chrono::system_clock::time_point > > google::cloud::CompletionQueue::MakeRelativeTimer ( std::chrono::duration< Rep, Period > duration ))"""; pugi::xml_document doc; doc.load_string(docfx_testing::FunctionXml().c_str()); auto selected = doc.select_node( ("//*[@id='" + docfx_testing::FunctionXmlId() + "']").c_str()); ASSERT_TRUE(selected); auto const actual = FunctionSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, Constructor) { auto constexpr kExpected = R"""(google::cloud::RuntimeStatusError::RuntimeStatusError ( Status status ))"""; pugi::xml_document doc; doc.load_string(kClassXml); auto selected = doc.select_node( "//*[@id='" "classgoogle_1_1cloud_1_1RuntimeStatusError" "_1aac6b78160cce6468696ce77eb1276a95']"); ASSERT_TRUE(selected); auto const actual = FunctionSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, Struct) { auto constexpr kExpected = R"""(// Found in #include struct google::cloud::AsyncTimerResult { ... };)"""; pugi::xml_document doc; doc.load_string(kStructXml); auto selected = doc.select_node("//*[@id='structgoogle_1_1cloud_1_1AsyncTimerResult']"); ASSERT_TRUE(selected); auto const actual = StructSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, Class) { auto constexpr kExpected = R"""(// Found in #include class google::cloud::RuntimeStatusError { ... };)"""; pugi::xml_document doc; doc.load_string(kClassXml); auto selected = doc.select_node("//*[@id='classgoogle_1_1cloud_1_1RuntimeStatusError']"); ASSERT_TRUE(selected); auto const actual = ClassSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, TemplateClass) { auto constexpr kXml = R"xml( google::cloud::StatusOr google/cloud/status_or.h typename T Holds a value or a Status indicating why there is no value. )xml"; auto constexpr kExpected = R"""(// Found in #include template < typename T> class google::cloud::StatusOr { ... };)"""; pugi::xml_document doc; doc.load_string(kXml); auto selected = doc.select_node("//*[@id='classgoogle_1_1cloud_1_1StatusOr']"); ASSERT_TRUE(selected); auto const actual = ClassSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, TemplateStruct) { auto constexpr kXml = R"xml( google::cloud::StatusOr google/cloud/status_or.h typename T Holds a value or a Status indicating why there is no value. )xml"; auto constexpr kExpected = R"""(// Found in #include template < typename T> struct google::cloud::StatusOr { ... };)"""; pugi::xml_document doc; doc.load_string(kXml); auto selected = doc.select_node("//*[@id='structgoogle_1_1cloud_1_1StatusOr']"); ASSERT_TRUE(selected); auto const actual = StructSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, Namespace) { auto constexpr kExpected = R"""(namespace google::cloud::mocks { ... };)"""; pugi::xml_document doc; doc.load_string(kNamespaceXml); auto selected = doc.select_node("//*[@id='namespacegoogle_1_1cloud_1_1mocks']"); ASSERT_TRUE(selected); auto const actual = NamespaceSyntaxContent(selected.node()); EXPECT_EQ(actual, kExpected); } TEST(Doxygen2Syntax, Enum) { auto constexpr kExpected = R"yml(syntax: contents: | enum class google::cloud::Idempotency { kIdempotent, kNonIdempotent, }; source: id: Idempotency path: google/cloud/idempotency.h startLine: 55 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/idempotency.h)yml"; pugi::xml_document doc; doc.load_string(kEnumXml); auto selected = doc.select_node( "//*[@id='" "namespacegoogle_1_1cloud_1a7d65fd569564712b7cfe652613f30d9c" "']"); ASSERT_TRUE(selected); YamlContext ctx; ctx.parent_id = "test-only-parent-id"; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendEnumSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } TEST(Doxygen2Syntax, Typedef) { auto constexpr kExpected = R"yml(syntax: contents: | using google::cloud::BackgroundThreadsFactory = std::function< std::unique_ptr< BackgroundThreads >()>; aliasof: | std::function< std::unique_ptr< BackgroundThreads >()> source: id: BackgroundThreadsFactory path: google/cloud/grpc_options.h startLine: 148 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/grpc_options.h)yml"; pugi::xml_document doc; doc.load_string(kTypedefXml); auto selected = doc.select_node( "//*[@id='" "namespacegoogle_1_1cloud_1a1498c1ea55d81842f37bbc42d003df1f" "']"); ASSERT_TRUE(selected); YamlContext ctx; ctx.parent_id = "test-only-parent-id"; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendTypedefSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } TEST(Doxygen2Syntax, Friend) { auto constexpr kXml = R"xml(xml( bool bool operator== (ErrorInfo const &, ErrorInfo const &) operator== google::cloud::ErrorInfo::operator== ErrorInfo const & ErrorInfo const & )xml"; auto constexpr kExpected = R"yml(syntax: contents: | friend bool google::cloud::ErrorInfo::operator== ( ErrorInfo const &, ErrorInfo const & ) source: id: operator== path: google/cloud/status.h startLine: 86 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/status.h)yml"; pugi::xml_document doc; doc.load_string(kXml); auto selected = doc.select_node( "//*[@id='" "classgoogle_1_1cloud_1_1ErrorInfo_1a3e7a9be9a728e13d3333784f63270555" "']"); ASSERT_TRUE(selected); YamlContext ctx; ctx.parent_id = "test-only-parent-id"; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendFriendSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } TEST(Doxygen2Syntax, Variable) { auto constexpr kExpected = R"yml(syntax: contents: | Severity severity; source: id: severity path: google/cloud/log.h startLine: 152 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/log.h)yml"; pugi::xml_document doc; doc.load_string(kVariableXml); auto selected = doc.select_node( "//*[@id='" "structgoogle_1_1cloud_1_1LogRecord_1a830f8e86e1581dddbbb2cd922cbc" "']"); ASSERT_TRUE(selected); YamlContext ctx; ctx.parent_id = "test-only-parent-id"; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendVariableSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } TEST(Doxygen2Syntax, Function) { auto constexpr kExpected = R"yml(syntax: contents: | template < typename Rep, typename Period> future< StatusOr< std::chrono::system_clock::time_point > > google::cloud::CompletionQueue::MakeRelativeTimer ( std::chrono::duration< Rep, Period > duration ) return: type: - "future< StatusOr< std::chrono::system_clock::time_point > >" description: | a future that becomes satisfied after `duration` time has elapsed. The result of the future is the time at which it expired, or an error [Status](xref:classgoogle_1_1cloud_1_1Status) if the timer did not run to expiration (e.g. it was cancelled). parameters: - id: duration var_type: "std::chrono::duration< Rep, Period >" description: | when should the timer expire relative to the current time. - id: typename Rep description: | a placeholder to match the Rep tparam for `duration` type, the semantics of this template parameter are documented in `std::chrono::duration<>` (in brief, the underlying arithmetic type used to store the number of ticks), for our purposes it is simply a formal parameter. - id: typename Period description: | a placeholder to match the Period tparam for `duration` type, the semantics of this template parameter are documented in `std::chrono::duration<>` (in brief, the length of the tick in seconds, expressed as a `std::ratio<>`), for our purposes it is simply a formal parameter. source: id: MakeRelativeTimer path: google/cloud/completion_queue.h startLine: 96 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/completion_queue.h)yml"; pugi::xml_document doc; doc.load_string(docfx_testing::FunctionXml().c_str()); auto selected = doc.select_node( ("//*[@id='" + docfx_testing::FunctionXmlId() + "']").c_str()); ASSERT_TRUE(selected); YamlContext ctx; ctx.parent_id = "test-only-parent-id"; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendFunctionSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } TEST(Doxygen2Syntax, Constructor) { auto constexpr kExpected = R"yml(syntax: contents: | google::cloud::RuntimeStatusError::RuntimeStatusError ( Status status ) parameters: - id: status var_type: "Status" source: id: RuntimeStatusError path: google/cloud/status.h startLine: 163 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/status.h)yml"; pugi::xml_document doc; doc.load_string(kClassXml); auto selected = doc.select_node( "//*[@id='" "classgoogle_1_1cloud_1_1RuntimeStatusError" "_1aac6b78160cce6468696ce77eb1276a95']"); ASSERT_TRUE(selected); YamlContext ctx; ctx.parent_id = "test-only-parent-id"; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendFunctionSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } TEST(Doxygen2SyntaxContent, FunctionException) { auto constexpr kXml = R"xml( T T google::cloud::future< T >::get () get google::cloud::future::get Waits until the shared state becomes ready, then retrieves the value stored in the shared state. This operation invalidates the future, subsequent calls will fail, the application should capture the returned value because it would. ... any exceptions stored in the shared state. std::future_error with std::no_state if the future does not have a shared state. )xml"; auto constexpr kExpected = R"""(syntax: contents: | T google::cloud::future::get () return: type: - "T" exceptions: - var_type: "..." description: | any exceptions stored in the shared state. - var_type: "std::future_error" description: | with std::no_state if the future does not have a shared state. source: id: get path: google/cloud/future_generic.h startLine: 84 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/future_generic.h)"""; pugi::xml_document doc; doc.load_string(kXml); auto selected = doc.select_node( "//" "*[@id='classgoogle_1_1cloud_1_1future_" "1a23b7c9cabdcf116d3b908c32e627c7af']"); ASSERT_TRUE(selected); YamlContext ctx; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendFunctionSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } TEST(Doxygen2Syntax, Class) { auto constexpr kExpected = R"yml(syntax: contents: | // Found in #include class google::cloud::RuntimeStatusError { ... }; source: id: google::cloud::RuntimeStatusError path: google/cloud/status.h startLine: 161 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/status.h)yml"; pugi::xml_document doc; doc.load_string(kClassXml); auto selected = doc.select_node("//*[@id='classgoogle_1_1cloud_1_1RuntimeStatusError']"); ASSERT_TRUE(selected); YamlContext ctx; ctx.parent_id = "test-only-parent-id"; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendClassSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } TEST(Doxygen2Syntax, Struct) { auto constexpr kExpected = R"yml(syntax: contents: | // Found in #include struct google::cloud::AsyncTimerResult { ... }; source: id: google::cloud::AsyncTimerResult path: google/cloud/async_operation.h startLine: 31 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/async_operation.h)yml"; pugi::xml_document doc; doc.load_string(kStructXml); auto selected = doc.select_node("//*[@id='structgoogle_1_1cloud_1_1AsyncTimerResult']"); ASSERT_TRUE(selected); YamlContext ctx; ctx.parent_id = "test-only-parent-id"; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendStructSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } TEST(Doxygen2Syntax, Namespace) { auto constexpr kExpected = R"yml(syntax: contents: | namespace google::cloud::mocks { ... }; source: id: google::cloud::mocks path: google/cloud/mocks/mock_stream_range.h startLine: 30 remote: repo: https://github.com/googleapis/google-cloud-cpp/ branch: main path: google/cloud/mocks/mock_stream_range.h)yml"; pugi::xml_document doc; doc.load_string(kNamespaceXml); auto selected = doc.select_node("//*[@id='namespacegoogle_1_1cloud_1_1mocks']"); ASSERT_TRUE(selected); YamlContext ctx; ctx.parent_id = "test-only-parent-id"; YAML::Emitter yaml; yaml << YAML::BeginMap; AppendNamespaceSyntax(yaml, ctx, selected.node()); yaml << YAML::EndMap; auto const actual = std::string{yaml.c_str()}; EXPECT_EQ(actual, kExpected); } } // namespace } // namespace docfx