Skip to content

Commit

Permalink
Fixing helper function to take care of OC paths containing values in …
Browse files Browse the repository at this point in the history
…[1/1] format. Implement portable Bazel test environment.
  • Loading branch information
kishanps authored and bibhuprasad-hcl committed Jul 18, 2024
1 parent 00e6fb9 commit e1865d6
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 3 deletions.
14 changes: 14 additions & 0 deletions lib/gnmi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ cc_library(
],
)

cc_test(
name = "gnmi_helper_test",
srcs = ["gnmi_helper_test.cc"],
deps = [
":gnmi_helper",
"//gutil:proto_matchers",
"//gutil:status_matchers",
"@com_github_gnmi//proto/gnmi:gnmi_cc_proto",
"@com_github_p4lang_p4runtime//:p4runtime_cc_grpc",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
)

proto_library(
name = "openconfig_proto",
srcs = ["openconfig.proto"],
Expand Down
22 changes: 19 additions & 3 deletions lib/gnmi/gnmi_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "lib/gnmi/gnmi_helper.h"

#include <cctype>
#include <string>
#include <utility>

Expand All @@ -39,13 +40,28 @@ namespace pins_test {
namespace {

using ::nlohmann::json;
const LazyRE2 kSplitNameValueRE = {R"((\w+)\[(\w+)\=(\w+)\])"};
// Splits string in format "component[name=integrated_circuit0]" to three
// tokens.
const LazyRE2 kSplitNameValueRE = {R"((\w+)\[(\w+)=([\S+]+)\])"};

// Splits string to tokens seperated by a char '/' as long as '/' is not
// included in [].
const LazyRE2 kSplitBreakSquareBraceRE = {R"(([^\[\/]+(\[[^\]]+\])?)\/?)"};

} // namespace

// This API generates gNMI path from OC path string.
// Example1:
// components/component[name=integrated_circuit0]/integrated-circuit/state.
// Example2:
// components/component[name=1/1]/integrated-circuit/state.
gnmi::Path ConvertOCStringToPath(absl::string_view oc_path) {
absl::string_view element;
std::vector<absl::string_view> elements;
while (RE2::FindAndConsume(&oc_path, *kSplitBreakSquareBraceRE, &element)) {
elements.push_back(element);
}
gnmi::Path path;
std::vector<absl::string_view> elements =
absl::StrSplit(oc_path, absl::ByChar('/'), absl::SkipEmpty());
for (absl::string_view node : elements) {
std::string key;
std::string attribute;
Expand Down
2 changes: 2 additions & 0 deletions lib/gnmi/gnmi_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,7 @@ absl::Status PushGnmiConfig(

absl::Status CheckAllInterfaceUpOverGnmi(gnmi::gNMI::Stub& stub);

// Returns gNMI Path for OC strings.
gnmi::Path ConvertOCStringToPath(absl::string_view oc_path);
} // namespace pins_test
#endif // GOOGLE_LIB_GNMI_GNMI_HELPER_H_
66 changes: 66 additions & 0 deletions lib/gnmi/gnmi_helper_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "lib/gnmi/gnmi_helper.h"

#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "gutil/proto_matchers.h"
#include "gutil/status_matchers.h"
#include "p4/v1/p4runtime.grpc.pb.h"
#include "proto/gnmi/gnmi.pb.h"

namespace pins_test {

namespace {
using gutil::EqualsProto;
} // namespace

TEST(OCStringToPath, OCStringToPathTestCase1) {
EXPECT_THAT(
ConvertOCStringToPath("interfaces/interface[name=ethernet0]/config/mtu"),
EqualsProto(R"pb(
elem { name: "interfaces" }
elem {
name: "interface"
key { key: "name" value: "ethernet0" }
}
elem { name: "config" }
elem { name: "mtu" }
)pb"));
}

TEST(OCStringToPath, OCStringToPathTestCase2) {
EXPECT_THAT(
ConvertOCStringToPath("components/component[name=1/1]/state/name"),
EqualsProto(R"pb(
elem { name: "components" }
elem {
name: "component"
key { key: "name" value: "1/1" }
}
elem { name: "state" }
elem { name: "name" }
)pb"));
}

TEST(OCStringToPath, OCStringToPathTestCase3) {
EXPECT_THAT(
ConvertOCStringToPath(
"interfaces/interface[name=ethernet0]/config/mtu/ic[name=1/1]/value"),
EqualsProto(R"pb(
elem { name: "interfaces" }
elem {
name: "interface"
key { key: "name" value: "ethernet0" }
}
elem { name: "config" }
elem { name: "mtu" }
elem {
name: "ic"
key { key: "name" value: "1/1" }
}
elem { name: "value" }
)pb"));
}

} // namespace pins_test
25 changes: 25 additions & 0 deletions thinkit/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,31 @@ cc_test(
],
)

cc_library(
name = "bazel_test_environment",
testonly = True,
srcs = ["bazel_test_environment.cc"],
hdrs = ["bazel_test_environment.h"],
deps = [
":test_environment",
"//gutil:status",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest",
],
)

cc_test(
name = "bazel_test_environment_test",
srcs = ["bazel_test_environment_test.cc"],
deps = [
":bazel_test_environment",
"//gutil:status_matchers",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
)

cc_library(
name = "ssh_client",
hdrs = ["ssh_client.h"],
Expand Down
95 changes: 95 additions & 0 deletions thinkit/bazel_test_environment.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) 2024, Google Inc.
//
// 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
//
// http://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 "thinkit/bazel_test_environment.h"

#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <ios>
#include <ostream>
#include <system_error> // NOLINT

#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "gtest/gtest.h"
#include "gutil/status.h"
#include "thinkit/test_environment.h"

namespace thinkit {

namespace {

absl::StatusOr<std::string> ArtifactDirectory() {
// Pick appropriate artifact directory using Bazel environment variables, see
// https://docs.bazel.build/versions/master/test-encyclopedia.html#initial-conditions
char* base_dir = std::getenv("TEST_UNDECLARED_OUTPUTS_DIR");
if (base_dir == nullptr) {
base_dir = std::getenv("TEST_TMPDIR");
}
if (base_dir == nullptr) {
return gutil::InternalErrorBuilder()
<< "Environment variables TEST_UNDECLARED_OUTPUTS_DIR and "
"TEST_TMPDIR undefined; is this a Bazel test run?";
}
std::string dir = base_dir;
if (auto* test_info = testing::UnitTest::GetInstance()->current_test_info();
test_info != nullptr) {
absl::StrAppend(&dir, "/", test_info->test_case_name(), "/",
test_info->name());
}

// Ensure the directory exists.
std::error_code error;
std::filesystem::create_directories(dir, error);
if (error) {
return gutil::InternalErrorBuilder()
<< "failed to create test artifact directory '" << dir
<< "': " << error;
}
return dir;
}

absl::Status WriteToTestArtifact(absl::string_view filename,
absl::string_view contents,
std::ios_base::openmode mode) {
ASSIGN_OR_RETURN(std::string directory, ArtifactDirectory());
std::string filepath = absl::StrCat(directory, "/", filename);
std::ofstream file;
file.open(filepath, mode);
if (!file.is_open()) {
return gutil::InternalErrorBuilder()
<< "unable to open test artifact file: '" << filepath << "'";
}
file << contents;
file.close();
if (file.good()) return absl::OkStatus();
return gutil::InternalErrorBuilder()
<< "failed to store test artifact: '" << filepath << "'";
}

} // namespace

absl::Status BazelTestEnvironment::StoreTestArtifact(
absl::string_view filename, absl::string_view contents) {
return WriteToTestArtifact(filename, contents, std::ios_base::trunc);
}

absl::Status BazelTestEnvironment::AppendToTestArtifact(
absl::string_view filename, absl::string_view contents) {
return WriteToTestArtifact(filename, contents, std::ios_base::app);
}

} // namespace thinkit
48 changes: 48 additions & 0 deletions thinkit/bazel_test_environment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) 2024, Google Inc.
//
// 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
//
// http://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.

#ifndef GOOGLE_THINKIT_BAZEL_TEST_ENVIRONMENT_H_
#define GOOGLE_THINKIT_BAZEL_TEST_ENVIRONMENT_H_

#include <ios>

#include "absl/strings/string_view.h"
#include "thinkit/test_environment.h"

namespace thinkit {

// Simple `thinkit::TestEnvironment` that works well with the Bazel build
// system.
class BazelTestEnvironment : public TestEnvironment {
public:
BazelTestEnvironment() = delete;
BazelTestEnvironment(bool mask_known_failures)
: mask_known_failures_{mask_known_failures} {}

absl::Status StoreTestArtifact(absl::string_view filename,
absl::string_view contents) override;

absl::Status AppendToTestArtifact(absl::string_view filename,
absl::string_view contents) override;


bool MaskKnownFailures() { return mask_known_failures_; };

private:
bool mask_known_failures_;
};

} // namespace thinkit

#endif // GOOGLE_THINKIT_BAZEL_TEST_ENVIRONMENT_H_
32 changes: 32 additions & 0 deletions thinkit/bazel_test_environment_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "thinkit/bazel_test_environment.h"

#include "absl/strings/string_view.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "gutil/status_matchers.h"

namespace thinkit {
namespace {

constexpr absl::string_view kTestArtifact = "my_test_artifact.txt";

class BazelTestEnvironmentTest : public testing::Test {
protected:
std::unique_ptr<TestEnvironment> environment_ =
absl::make_unique<BazelTestEnvironment>(/*mask_known_failures=*/true);
};

TEST_F(BazelTestEnvironmentTest, StoreTestArtifact) {
EXPECT_OK(environment_->StoreTestArtifact(kTestArtifact, "Hello, World!\n"));
EXPECT_OK(environment_->StoreTestArtifact(kTestArtifact, "Hello, Test!\n"));
}

TEST_F(BazelTestEnvironmentTest, AppendToTestArtifact) {
EXPECT_OK(
environment_->AppendToTestArtifact(kTestArtifact, "Hello, World!\n"));
EXPECT_OK(
environment_->AppendToTestArtifact(kTestArtifact, "Hello, Test!\n"));
}

} // namespace
} // namespace thinkit

0 comments on commit e1865d6

Please sign in to comment.