Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Comb] Fixing helper function to take care of OC paths containing values in [1/1] format. Implement portable Bazel test environment. #349

Merged
merged 1 commit into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading