// Copyright(c) 2018-2019, Intel Corporation
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of Intel Corporation nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <opae/fpga.h>
#include <libbitstream/bitstream.h>
#include "gtest/gtest.h"
#include "mock/test_system.h"
#include "mock/test_utils.h"
extern "C" {
#include <json-c/json.h>
#include <uuid/uuid.h>
struct config {
unsigned int verbosity;
bool dry_run;
enum { INTERACTIVE, /* ask if ambiguous */
NORMAL, /* stop if ambiguous */
AUTOMATIC /* choose if ambiguous */
} mode;
int flags;
struct target {
int segment;
int bus;
int device;
int function;
int socket;
} target;
char *filename;
};
extern struct config config;
void help(void);
void print_err(const char *s, fpga_result res);
void print_msg(unsigned int verbosity, const char *s);
int parse_args(int argc, char *argv[]);
int print_interface_id(fpga_guid actual_interface_id);
int find_fpga(fpga_guid interface_id, fpga_token *fpga);
int program_bitstream(fpga_token token, uint32_t slot_num,
opae_bitstream_info *info, int flags);
int fpgaconf_main(int argc, char *argv[]);
}
fpga_guid test_guid = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
#include <config.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <unistd.h>
using namespace opae::testing;
class fpgaconf_c_p : public ::testing::TestWithParam<std::string> {
protected:
fpgaconf_c_p() {}
virtual void SetUp() override {
strcpy(tmp_gbs_, "tmp-XXXXXX.gbs");
close(mkstemps(tmp_gbs_, 4));
std::string platform_key = GetParam();
ASSERT_TRUE(test_platform::exists(platform_key));
platform_ = test_platform::get(platform_key);
system_ = test_system::instance();
system_->initialize();
system_->prepare_syfs(platform_);
EXPECT_EQ(fpgaInitialize(NULL), FPGA_OK);
// assemble valid bitstream header
auto fme_guid = platform_.devices[0].fme_guid;
auto afu_guid = platform_.devices[0].afu_guid;
auto bitstream_j = jobject
("version", int32_t(1))
("afu-image", jobject
("clock-frequency-high", int32_t(312))
("clock-frequency-low", int32_t(156))
("power", int32_t(50))
("interface-uuid", fme_guid)
("magic-no", int32_t(488605312))
("accelerator-clusters", {
jobject
("total-contexts", int32_t(1))
("name", "nlb")
("accelerator-type-uuid", afu_guid)
}
)
)
("platform-name", "platformX");
bitstream_valid_ = system_->assemble_gbs_header(platform_.devices[0], bitstream_j.c_str());
bitstream_j.put();
std::ofstream gbs;
gbs.open(tmp_gbs_, std::ios::out|std::ios::binary);
gbs.write((const char *) bitstream_valid_.data(), bitstream_valid_.size());
gbs.close();
optind = 0;
config_ = config;
}
virtual void TearDown() override {
config = config_;
fpgaFinalize();
system_->finalize();
if (!::testing::Test::HasFatalFailure() &&
!::testing::Test::HasNonfatalFailure()) {
unlink(tmp_gbs_);
}
}
void copy_bitstream(std::string path) {
copy_gbs_ = path;
std::ifstream src(tmp_gbs_, std::ios::binary);
std::ofstream dst(copy_gbs_, std::ios::binary);
dst << src.rdbuf();
}
char tmp_gbs_[20];
std::string copy_gbs_;
std::vector<uint8_t> bitstream_valid_;
struct config config_;
test_platform platform_;
test_system *system_;
};
/**
* @test help
* @brief Test: help
* @details help displays the application help message.<br>
*/
TEST_P(fpgaconf_c_p, help) {
help();
}
/**
* @test print_err
* @brief Test: print_err
* @details print_err sends the given string and a decoding of the fpga_result<br>
* to stderr.<br>
*/
TEST_P(fpgaconf_c_p, print_err) {
print_err("msg", FPGA_OK);
}
/**
* @test print_msg
* @brief Test: print_msg
* @details print_msg sends the given string to stdout<br>
* if the given verbosity is less than or equal config.verbosity.<br>
*/
TEST_P(fpgaconf_c_p, print_msg) {
print_msg(0, "msg");
}
/**
* @test parse_args0
* @brief Test: parse_args
* @details When given an invalid command option,<br>
* parse_args returns a negative value.<br>
*/
TEST_P(fpgaconf_c_p, parse_args0) {
char zero[20];
char one[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-Y");
char *argv[] = { zero, one };
EXPECT_LT(parse_args(2, argv), 0);
}
/**
* @test parse_args1
* @brief Test: parse_args
* @details When given valid command options,<br>
* parse_args populates the global config struct<br>
* and the fn returns 0.<br>
*/
TEST_P(fpgaconf_c_p, parse_args1) {
char tmpfilename[] = "tmp-empty-XXXXXX.gbs";
close(mkstemps(tmpfilename, 4));
char zero[20];
char one[20];
char two[20];
char three[20];
char four[20];
char five[20];
char six[20];
char seven[20];
char eight[20];
char nine[20];
char ten[20];
char eleven[20];
char twelve[20];
char thirteen[20];
char fourteen[20];
char fifteen[20];
char sixteen[30];
strcpy(zero, "fpgaconf");
strcpy(one, "-V");
strcpy(two, "-n");
strcpy(three, "--force");
strcpy(four, "--segment");
strcpy(five, "0x1234");
strcpy(six, "-B");
strcpy(seven, "0x5e");
strcpy(eight, "-D");
strcpy(nine, "0xab");
strcpy(ten, "-F");
strcpy(eleven, "3");
strcpy(twelve, "-S");
strcpy(thirteen, "2");
strcpy(fourteen, "-A");
strcpy(fifteen, "-I");
strcpy(sixteen, tmpfilename);
char *argv[] = { zero, one, two, three, four,
five, six, seven, eight, nine,
ten, eleven, twelve, thirteen, fourteen,
fifteen, sixteen };
EXPECT_EQ(parse_args(17, argv), 0);
EXPECT_EQ(config.verbosity, 1);
EXPECT_NE(config.dry_run, 0);
EXPECT_NE(config.flags & FPGA_RECONF_FORCE, 0);
EXPECT_EQ(config.target.segment, 0x1234);
EXPECT_EQ(config.target.bus, 0x5e);
EXPECT_EQ(config.target.device, 0xab);
EXPECT_EQ(config.target.function, 3);
EXPECT_EQ(config.target.socket, 2);
EXPECT_EQ(config.mode, 0);
ASSERT_NE(config.filename, nullptr);
EXPECT_STREQ(basename(config.filename), tmpfilename);
free(config.filename);
unlink(tmpfilename);
}
/**
* @test parse_args2
* @brief Test: parse_args
* @details When given "-h",<br>
* parse_args prints the help message and returns a negative value.<br>
*/
TEST_P(fpgaconf_c_p, parse_args2) {
char zero[20];
char one[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-h");
char *argv[] = { zero, one };
EXPECT_LT(parse_args(2, argv), 0);
}
/**
* @test invalid_parse_args1
* @brief Test: parse_args
* @details When given an invalid command options,<br>
* parse_args populates the global config struct<br>
* and the fn returns -1.<br>
*/
TEST_P(fpgaconf_c_p, invalid_parse_args1) {
char zero[32];
char one[32];
char two[32];
char three[36];
char four[32];
char five[32];
char six[32];
char seven[32];
char eight[32];
char nine[32];
char ten[48];
char eleven[32];
char twelve[32];
char thirteen[32];
strcpy(zero, " fpgaconf Q&%^#;'kk/");
strcpy(one, "-verbosesss \n\t\b\e\a\?");
strcpy(two, "--n");
strcpy(three, "--f123 23ksfa;.'/'l|hrce");
strcpy(four, "--se!gmentt lsdfhskfa");
strcpy(five, "0x1234");
strcpy(six, "-bussssss");
strcpy(seven, "0x5e");
strcpy(eight, "-Devic\xF0\x90sss \t\n\b\a\e\v");
strcpy(nine, "0xab");
strcpy(ten, " =====%34 -Function \x09\x0B\x0D");
strcpy(eleven, "3");
strcpy(twelve, "-Socket__ \xF1-\xF3 \x8F");
strcpy(thirteen, "2");
char *argv[] = { zero, one, two, three, four,
five, six, seven, eight, nine,
ten, eleven, twelve, thirteen };
EXPECT_LT(parse_args(14, argv), 0);
}
/**
* @test invalid_parse_args2
* @brief Test: parse_args
* @details When given an invalid command options,<br>
* parse_args populates the global config struct<br>
* and the fn returns -1.<br>
*/
TEST_P(fpgaconf_c_p, invalid_parse_args2) {
char zero[20];
char one[20];
char two[20];
char three[20];
char four[20];
char five[20];
char six[20];
char seven[20];
char eight[20];
char nine[20];
char ten[20];
char eleven[20];
char twelve[20];
char thirteen[20];
char fourteen[20];
strcpy(zero, " ");
strcpy(one, "-v");
strcpy(two, "-n");
strcpy(three, "--force");
strcpy(four, "--segment");
strcpy(five, "0xffff1234");
strcpy(six, "-B");
strcpy(seven, "0x5eeeeeeeee");
strcpy(eight, "-D");
strcpy(nine, "0xab124 \xF1 0");
strcpy(ten, "-F");
strcpy(eleven, "-33492\t000");
strcpy(twelve, "-S");
strcpy(thirteen, "\000 00000000");
strcpy(fourteen, "-A");
char *argv[] = { zero, one, two, three,
ten, eleven, twelve, thirteen, fourteen,
four, five, six, seven, eight, nine};
EXPECT_LT(parse_args(15, argv), 0);
}
/*
* @test parse_args3
* @brief Test: parse_args
* @details When given a gbs file that does not exist<br>
* parse_args fails at parsing the command line<br>
* and the fn returns non-zero value
*/
TEST_P(fpgaconf_c_p, parse_args3) {
const char *argv[] = { "fpgaconf", "no-file.gbs" };
EXPECT_NE(parse_args(2, (char**)argv), 0);
}
/**
* @test ifc_id1
* @brief Test: print_interface_id
* @details When the given config.target settings match no device,<br>
* print_interface_id returns 0.<br>
*/
TEST_P(fpgaconf_c_p, ifc_id1) {
config.target.bus = 0xff;
EXPECT_EQ(print_interface_id(test_guid), 0);
}
/**
* @test find_fpga0
* @brief Test: find_fpga
* @details When the given config.target settings match no device,<br>
* find_fpga returns 0.<br>
*/
TEST_P(fpgaconf_c_p, find_fpga0) {
config.target.bus = 0xff;
fpga_token tok = nullptr;
EXPECT_EQ(find_fpga(test_guid, &tok), 0);
EXPECT_EQ(tok, nullptr);
}
/**
* @test main0
* @brief Test: fpgaconf_main
* @details When the command params are invalid,<br>
* fpgaconf_main returns 1.<br>
*/
TEST_P(fpgaconf_c_p, main0) {
char zero[20];
char one[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-Y");
char *argv[] = { zero, one };
EXPECT_EQ(fpgaconf_main(2, argv), 1);
}
/**
* @test main1
* @brief Test: fpgaconf_main
* @details When the command params are valid,<br>
* and they identify a valid accelerator device,<br>
* fpgaconf_main loads the bitstream, finds the device,<br>
* and PR's it, returning 0.<br>
*/
TEST_P(fpgaconf_c_p, main1) {
char zero[20];
char one[20];
char two[20];
char three[20];
char four[20];
char five[20];
char six[20];
char seven[20];
char eight[20];
char nine[20];
char ten[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-n");
strcpy(two, "--segment");
sprintf(three, "%d", platform_.devices[0].segment);
strcpy(four, "-B");
sprintf(five, "%d", platform_.devices[0].bus);
strcpy(six, "-D");
sprintf(seven, "%d", platform_.devices[0].device);
strcpy(eight, "-F");
sprintf(nine, "%d", platform_.devices[0].function);
strcpy(ten, tmp_gbs_);
char *argv[] = { zero, one, two, three, four,
five, six, seven, eight, nine,
ten };
EXPECT_EQ(fpgaconf_main(11, argv), 0);
}
/**
* @test main2
* @brief Test: fpgaconf_main
* @details When the command params are valid,<br>
* but no valid accelerator device can be found,<br>
* fpgaconf_main displays an error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, main2) {
char zero[20];
char one[20];
char two[20];
char three[20];
char four[20];
char five[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-v");
strcpy(two, "-n");
strcpy(three, "-B");
strcpy(four, "0xff");
strcpy(five, tmp_gbs_);
char *argv[] = { zero, one, two, three, four,
five };
EXPECT_NE(fpgaconf_main(6, argv), 0);
}
/**
* @test main_seg_neg
* @brief Test: fpgaconf_main
* @details When the command params for sement are invalid,<br>
* fpgaconf_main displays an error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, main_seg_neg) {
char zero[20];
char one[20];
char two[20];
strcpy(zero, "fpgaconf");
strcpy(one, "--segment");
strcpy(two, "k");
char *argv[] = { zero, one, two };
EXPECT_NE(fpgaconf_main(3, argv), 0);
}
/**
* @test main_bus_neg
* @brief Test: fpgaconf_main
* @details When the command params for bus are invalid,<br>
* fpgaconf_main displays an error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, main_bus_neg) {
char zero[20];
char one[20];
char two[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-B");
strcpy(two, "k");
char *argv[] = { zero, one, two };
EXPECT_NE(fpgaconf_main(3, argv), 0);
}
/**
* @test main_dev_neg
* @brief Test: fpgaconf_main
* @details When the command params for device are invalid,<br>
* fpgaconf_main displays an error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, main_dev_neg) {
char zero[20];
char one[20];
char two[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-D");
strcpy(two, "k");
char *argv[] = { zero, one, two };
EXPECT_NE(fpgaconf_main(3, argv), 0);
}
/**
* @test main_soc_neg
* @brief Test: fpgaconf_main
* @details When the command params for socket are invalid,<br>
* fpgaconf_main displays an error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, main_soc_neg) {
char zero[20];
char one[20];
char two[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-S");
strcpy(two, "k");
char *argv[] = { zero, one, two };
EXPECT_NE(fpgaconf_main(3, argv), 0);
}
/**
* @test main_missing_arg
* @brief Test: fpgaconf_main
* @details When the command params is missing an argument,<br>
* fpgaconf_main displays an error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, main_missing_arg) {
char zero[20];
char one[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-S");
char *argv[] = { zero, one };
EXPECT_NE(fpgaconf_main(2, argv), 0);
}
/**
* @test main_missing_gbs
* @brief Test: fpgaconf_main
* @details When the command params is missing the gbs param,<br>
* fpgaconf_main displays an error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, main_missing_gbs) {
char zero[20];
strcpy(zero, "fpgaconf");
char *argv[] = { zero };
EXPECT_NE(fpgaconf_main(1, argv), 0);
}
/**
* @test embed_nullchar
* @brief Test: fpgaconf_main
* @details When the command params contains nullbyte,<br>
* fpgaconf_main displays an error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, embed_nullchar1) {
copy_bitstream("copy_bitstream.gbs");
const char *argv[] = { "fpgaconf", "-B", "0x5e", "copy_bitstream\0.gbs"};
EXPECT_NE(fpgaconf_main(4, (char**)argv), 0);
unlink(copy_gbs_.c_str());
}
TEST_P(fpgaconf_c_p, embed_nullchar2) {
copy_bitstream("copy_bitstream.gbs");
const char *argv[] = { "fpgaconf", "-B", "0x5e", "\0 copy_bitstream.gbs"};
EXPECT_NE(fpgaconf_main(4, (char**)argv), 0);
unlink(copy_gbs_.c_str());
}
/**
* @test encoding_path
* @brief Test: fpgaconf_main
* @details When command param is encoding path,<br>
* fpgaconf_main displays file error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, encoding_path) {
copy_bitstream("copy_bitstream.gbs");
char zero[32];
char one[32];
char two[32];
char three[40];
strcpy(zero, "fpgaconf");
strcpy(one, "-B");
strcpy(two, "0x5e");
char *argv[] = { zero, one, two, three};
// File not found
strcpy(three, "copy_bitstream%2egbs");
EXPECT_NE(fpgaconf_main(4, argv), 0);
// File not found
memset(three, 0, sizeof(three));
strcpy(three, "copy_bitstream..gbs");
EXPECT_NE(fpgaconf_main(4, argv), 0);
// File not found
memset(three, 0, sizeof(three));
strcpy(three, "....copy_bitstream.gbs");
EXPECT_NE(fpgaconf_main(4, argv), 0);
// File not found
memset(three, 0, sizeof(three));
strcpy(three, "%252E%252E%252Fcopy_bitstream.gbs");
EXPECT_NE(fpgaconf_main(4, argv), 0);
unlink(copy_gbs_.c_str());
}
/**
* @test relative_path
* @brief Test: fpgaconf_main
* @details When gbs file locates in parent directory and command params<br>
* contains path traversal. On success, fpgaconf_main loads bitstream<br>
* and returns zero. Otherwise, it displays file error and returns non-zero.<br>
*/
TEST_P(fpgaconf_c_p, relative_path) {
copy_bitstream("../copy_bitstream.gbs");
char zero[32];
char one[32];
char two[32];
char three[32];
char four[32];
strcpy(zero, "fpgaconf");
strcpy(one, "-n");
strcpy(two, "-B");
strcpy(three, "0x5e");
char *argv[] = { zero, one, two, three, four};
strcpy(four, "../copy_bitstream.gbs");
EXPECT_EQ(fpgaconf_main(5, argv), 0);
// Fail not found
memset(four, 0, sizeof(four));
strcpy(four, "../..../copy_bitstream.gbs");
EXPECT_NE(fpgaconf_main(5, argv), 0);
// Fail not found
memset(four, 0, sizeof(four));
strcpy(four, "..%2fcopy_bitstream.gbs");
EXPECT_NE(fpgaconf_main(5, argv), 0);
// Fail not found
memset(four, 0, sizeof(four));
strcpy(four, "%2e%2e/copy_bitstream.gbs");
EXPECT_NE(fpgaconf_main(5, argv), 0);
// Fail not found
memset(four, 0, sizeof(four));
strcpy(four, "%2e%2e%2fcopy_bitstream.gbs");
EXPECT_NE(fpgaconf_main(5, argv), 0);
unlink(copy_gbs_.c_str());
}
/**
* @test absolute_path_pos
* @brief Test: fpgaconf_main
* @details When the command params are valid with absolute gbs path,<br>
* fpgaconf_main loads in bitstream and returns 0.<br>
*/
TEST_P(fpgaconf_c_p, absolute_path_pos) {
copy_bitstream("copy_bitstream.gbs");
char zero[32];
char one[32];
char two[32];
char three[32];
char four[128];
strcpy(zero, "fpgaconf");
strcpy(one, "-n");
strcpy(two, "-B");
strcpy(three, "0x5e");
char *argv[] = { zero, one, two, three, four};
char *current_path = get_current_dir_name();
std::string bitstream_path = (std::string)current_path + "/copy_bitstream.gbs";
strcpy(four, bitstream_path.c_str());
EXPECT_EQ(fpgaconf_main(5, argv), 0);
free(current_path);
unlink(copy_gbs_.c_str());
}
/**
* @test absolute_path_neg
* @brief Test: fpgaconf_main
* @details When the command params are valid but bitstream data is,<br>
* invalid, fpgaconf_main loads in bitstream and fails to <br>
* set userclock, it returns none-zero.<br>
*/
TEST_P(fpgaconf_c_p, absolute_path_neg) {
bitstream_valid_ = system_->assemble_gbs_header(platform_.devices[0]);
std::ofstream gbs;
gbs.open(tmp_gbs_, std::ios::out|std::ios::binary);
gbs.write((const char *) bitstream_valid_.data(), bitstream_valid_.size());
gbs.close();
copy_bitstream("copy_bitstream.gbs");
char zero[32];
char one[32];
char two[32];
char three[128];
strcpy(zero, "fpgaconf");
strcpy(one, "-B");
strcpy(two, "0x5e");
char *argv[] = { zero, one, two, three};
char *current_path = get_current_dir_name();
std::string bitstream_path = (std::string)current_path + "/copy_bitstream.gbs";
strcpy(three, bitstream_path.c_str());
EXPECT_NE(fpgaconf_main(4, argv), 0);
free(current_path);
unlink(copy_gbs_.c_str());
}
/**
* @test read_symlink_bs
* @brief Test:
* @details Tests for symlink on gbs file. When successful,<br>
* fpgaconf_main loads the bitstream into the gbs_data field<br>
* and the fn returns 0. When file doesn't exist, fpgaconf_main<br>
* displays error and returns -1.<br>
*/
TEST_P(fpgaconf_c_p, main_symlink_bs) {
copy_bitstream("copy_bitstream.gbs");
const std::string symlink_gbs = "bits_symlink";
char zero[20];
char one[20];
char two[20];
char three[20];
char four[20];
char five[20];
strcpy(zero, "fpgaconf");
strcpy(one, "-V");
strcpy(two, "-n");
strcpy(three, "-B");
strcpy(four, "0x5e");
char *argv[] = { zero, one, two, three, four,
five };
auto ret = symlink(copy_gbs_.c_str(), symlink_gbs.c_str());
EXPECT_EQ(ret, 0);
// Success case
strcpy(five, symlink_gbs.c_str());
EXPECT_EQ(fpgaconf_main(6, argv), 0);
// remove bitstream file
unlink(copy_gbs_.c_str());
// Fail case
EXPECT_NE(fpgaconf_main(6, argv), 0);
unlink(symlink_gbs.c_str());
}
/**
* @test circular_symlink
* @brief Test: fpgaconf_c_p
* @details Tests for circular symlink on gbs file. <br>
* fpgaconf_c_p displays error and returns -1.<br>
*/
TEST_P(fpgaconf_c_p, circular_symlink) {
const std::string symlink_A = "./link1/bits_symlink_A";
const std::string symlink_B = "./link2/bits_symlink_B";
char zero[20];
char one[20];
char two[20];
char three[20];
char four[20];
char five[32];
strcpy(zero, "fpgaconf");
strcpy(one, "-v");
strcpy(two, "-n");
strcpy(three, "-B");
strcpy(four, "0x5e");
char *argv[] = { zero, one, two, three, four,
five };
// Create link directories
auto ret = mkdir("./link1", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
EXPECT_EQ(ret, 0);
ret = mkdir("./link2", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
EXPECT_EQ(ret, 0);
// Create circular symlinks
ret = symlink("link1", symlink_B.c_str());
EXPECT_EQ(ret, 0);
ret = symlink("link2", symlink_A.c_str());
EXPECT_EQ(ret, 0);
strcpy(five, symlink_A.c_str());
EXPECT_NE(fpgaconf_main(6, argv), 0);
memset(five, 0, sizeof(five));
strcpy(five, symlink_B.c_str());
EXPECT_NE(fpgaconf_main(6, argv), 0);
// Clean up
unlink(symlink_A.c_str());
unlink(symlink_B.c_str());
remove("link1");
remove("link2");
}
INSTANTIATE_TEST_CASE_P(fpgaconf_c, fpgaconf_c_p,
::testing::ValuesIn(test_platform::platforms({"skx-p"})));
class fpgaconf_c_mock_p : public fpgaconf_c_p{
protected:
fpgaconf_c_mock_p(){}
};
/**
* @test ifc_id0
* @brief Test: print_interface_id
* @details When the config.target struct is populated with<br>
* bus, device, function, and socket,<br>
* print_interface_id uses those settings for enumeration,<br>
* returning the number of matches found.<br>
*/
TEST_P(fpgaconf_c_mock_p, ifc_id0) {
config.target.segment = platform_.devices[0].segment;
config.target.bus = platform_.devices[0].bus;
config.target.device = platform_.devices[0].device;
config.target.function = platform_.devices[0].function;
config.target.socket = platform_.devices[0].socket_id;
EXPECT_EQ(print_interface_id(test_guid), 1);
}
/**
* @test find_fpga1
* @brief Test: find_fpga
* @details When the config.target struct is populated with<br>
* bus, device, function, and socket,<br>
* find_fpga uses those settings in conjunction with the
* given PR interface ID for enumeration,<br>
* returning the number of matches found.<br>
*/
TEST_P(fpgaconf_c_mock_p, find_fpga1) {
config.target.segment = platform_.devices[0].segment;
config.target.bus = platform_.devices[0].bus;
config.target.device = platform_.devices[0].device;
config.target.function = platform_.devices[0].function;
config.target.socket = platform_.devices[0].socket_id;
fpga_guid pr_ifc_id;
ASSERT_EQ(uuid_parse(platform_.devices[0].fme_guid, pr_ifc_id), 0);
fpga_token tok = nullptr;
EXPECT_EQ(find_fpga(pr_ifc_id, &tok), 1);
ASSERT_NE(tok, nullptr);
EXPECT_EQ(fpgaDestroyToken(&tok), FPGA_OK);
}
/**
* @test prog_bs0
* @brief Test: program_bitstream
* @details When config.dry_run is set to true,<br>
* program_bitstream skips the PR step,<br>
* and the fn returns 1.<br>
*/
TEST_P(fpgaconf_c_mock_p, prog_bs0) {
config.target.segment = platform_.devices[0].segment;
config.target.bus = platform_.devices[0].bus;
config.target.device = platform_.devices[0].device;
config.target.function = platform_.devices[0].function;
config.target.socket = platform_.devices[0].socket_id;
config.dry_run = true;
fpga_guid pr_ifc_id;
ASSERT_EQ(uuid_parse(platform_.devices[0].fme_guid, pr_ifc_id), 0);
opae_bitstream_info info;
ASSERT_EQ(opae_load_bitstream(tmp_gbs_, &info), FPGA_OK);
fpga_token tok = nullptr;
EXPECT_EQ(find_fpga(pr_ifc_id, &tok), 1);
ASSERT_NE(tok, nullptr);
EXPECT_EQ(program_bitstream(tok, 0, &info, 0), 1);
EXPECT_EQ(opae_unload_bitstream(&info), FPGA_OK);
EXPECT_EQ(fpgaDestroyToken(&tok), FPGA_OK);
}
/**
* @test prog_bs1
* @brief Test: program_bitstream
* @details When config.dry_run is set to false,<br>
* program_bitstream attempts the PR,<br>
* but fails to set user clocks,<br>
* causing the function to return -1.<br>
*/
TEST_P(fpgaconf_c_mock_p, prog_bs1) {
config.target.segment = platform_.devices[0].segment;
config.target.bus = platform_.devices[0].bus;
config.target.device = platform_.devices[0].device;
config.target.function = platform_.devices[0].function;
config.target.socket = platform_.devices[0].socket_id;
ASSERT_EQ(config.dry_run, false);
fpga_guid pr_ifc_id;
ASSERT_EQ(uuid_parse(platform_.devices[0].fme_guid, pr_ifc_id), 0);
opae_bitstream_info info;
ASSERT_EQ(opae_load_bitstream(tmp_gbs_, &info), FPGA_OK);
fpga_token tok = nullptr;
EXPECT_EQ(find_fpga(pr_ifc_id, &tok), 1);
ASSERT_NE(tok, nullptr);
EXPECT_EQ(program_bitstream(tok, 0, &info, 0), -1);
EXPECT_EQ(opae_unload_bitstream(&info), FPGA_OK);
EXPECT_EQ(fpgaDestroyToken(&tok), FPGA_OK);
}
/**
* @test prog_bs2
* @brief Test: program_bitstream
* @details When config.dry_run is set to false,<br>
* program_bitstream attempts the PR,<br>
* which fails when given an invalid bitstream,<br>
* causing the function to return -1.<br>
*/
TEST_P(fpgaconf_c_mock_p, prog_bs2) {
bitstream_valid_ = system_->assemble_gbs_header(platform_.devices[0]);
std::ofstream gbs;
gbs.open(tmp_gbs_, std::ios::out|std::ios::binary);
gbs.write((const char *) bitstream_valid_.data(), bitstream_valid_.size());
gbs.close();
config.target.segment = platform_.devices[0].segment;
config.target.bus = platform_.devices[0].bus;
config.target.device = platform_.devices[0].device;
config.target.function = platform_.devices[0].function;
config.target.socket = platform_.devices[0].socket_id;
ASSERT_EQ(config.dry_run, false);
fpga_guid pr_ifc_id;
ASSERT_EQ(uuid_parse(platform_.devices[0].fme_guid, pr_ifc_id), 0);
opae_bitstream_info info;
ASSERT_EQ(opae_load_bitstream(tmp_gbs_, &info), FPGA_OK);
fpga_token tok = nullptr;
EXPECT_EQ(find_fpga(pr_ifc_id, &tok), 1);
ASSERT_NE(tok, nullptr);
EXPECT_EQ(program_bitstream(tok, 0, &info, 0), -1);
EXPECT_EQ(opae_unload_bitstream(&info), FPGA_OK);
EXPECT_EQ(fpgaDestroyToken(&tok), FPGA_OK);
}
INSTANTIATE_TEST_CASE_P(fpgaconf_c, fpgaconf_c_mock_p,
::testing::ValuesIn(test_platform::mock_platforms({"skx-p"})));