Google Test(GTest)使用方法和源码解析——参数自动填充技术分析和应用
在我们设计测试用例时,我们需要考虑很多场景。每个场景都可能要细致地考虑到到各个参数的选择。比如我们希望使用函数IsPrime检测10000以内字的数字,难道我们要写一万行代码么?(转载请指明出于breaksoftware的csdn博客)
EXPECT_TRUE(IsPrime(0));
EXPECT_TRUE(IsPrime(1));
EXPECT_TRUE(IsPrime(2));
......
EXPECT_TRUE(IsPrime(9999));
这种写法明显是不合理的。GTest框架当然也会考虑到这点,它设计了一套自动生成上述检测的机制,让我们用很少的代码就可以解决这个问题。
参数自动填充机制应用
我们先从应用的角度讲解其使用。首先我们设计一个需要被测试的类
class Bis {
public:bool Even(int n) {if (n % 2 == 0) {return true;}else {return false;}};bool Suc(bool suc) {return suc;}
};
该类暴露了两个返回bool类型的方法:Even用于判断是否是偶数;Suc只是返回传入的参数。
由于GTest要求提供测试的类要继承于::testing::Test,于是我们定义一个代理类,它只是继承于::testing::Test和Bis,代理Bis完成相关调用。
class TestClass : public Bis,public ::testing::Test {
};
bool型入参
Suc函数的入参类型是bool,于是我们可以新建一个测试用例类,让它继承于template <typename T> class WithParamInterface模板类,并把模板指定为bool
class CheckBisSuc :public TestClass,public ::testing::WithParamInterface<bool>
{
};
我们再设置一个测试特例,在特例中使用GetParam()方法获取框架指定的参数
TEST_P(CheckBisSuc, Test) {EXPECT_TRUE(Suc(GetParam()));
}
最后,我们使用INSTANTIATE_TEST_CASE_P宏向框架注册“定制化测试”
INSTANTIATE_TEST_CASE_P(TestBisBool, CheckBisSuc, Bool());
该宏的第一个参数是测试前缀,第二个参数是测试类名,第三个参数是参数生成规则。如此我们就相当于执行了
EXPECT_TRUE(Suc(true));EXPECT_TRUE(Suc(false));
可选择入参
我们再看下针对Even函数的测试。我们要定义一个继承于template <typename T> class WithParamInterface模板类的类CheckBisEven,用于指定Even的入参类型为int
class CheckBisEven :public TestClass,public ::testing::WithParamInterface<int>
{
};
然后我们建立一个针对该类的测试特例
TEST_P(CheckBisEven, Test) {EXPECT_TRUE(Even(GetParam()));
}
最后我们可以使用Range、Values或者ValuesIn的方式指定Even的参数值
INSTANTIATE_TEST_CASE_P(TestBisValuesRange, CheckBisEven, Range(0, 9, 2));INSTANTIATE_TEST_CASE_P(TestBisValues, CheckBisEven, Values(11, 12, 13, 14));int values[] = {0, 1};
INSTANTIATE_TEST_CASE_P(TestBisValuesIn, CheckBisEven, ValuesIn(values));int moreValues[] = {0,1,2,3,4,5,6,7,8,9,10};
vector<int> IntVecValues(moreValues, moreValues + sizeof(moreValues));
INSTANTIATE_TEST_CASE_P(TestBisValuesInVector, CheckBisEven, ValuesIn(IntVecValues));
Range的第一个参数是起始参数值,第二个值是结束参数值,第三个参数是递增值。于是Range这组测试测试的是0、2、4、6、8这些入参。如果第三个参数没有, 则默认是递增1。
Values中罗列的是将被选择作为参数的值。
ValuesIn的参数是个容器或者容器的起始迭代器和结束迭代器。
参数组合
参数组合要求编译器支持tr/tuple,所以一些不支持tr库的编译器将无法使用该功能。
什么是参数组合?顾名思义,就是将不同参数集组合在一起衍生出多维的数据。比如(true,false)和(1,2)可以组合成(true,1)、(true,2)、(false,1)和(false,2)等四种参数组合,然后我们使用这四组数据进行测试。
我们看个例子,首先我们要定义一个待测类。需要注意的是,它继承了模板类TestWithParam,且模板参数是组合的类型::testing::tuple<bool, int>。这个类并没有继承Bis,而是让Bis成为其成员变量,在checkData函数中检测Bis的各个函数
class CombineTest : public TestWithParam< ::testing::tuple<bool, int> > {
protected:bool checkData() {bool suc = ::testing::get<0>(GetParam());int n = ::testing::get<1>(GetParam());return bis.Suc(suc) && bis.Even(n);}
private:Bis bis;
};
然后我们定义一个(true,false)和(1,2,3,4)组合测试
TEST_P(CombineTest, Test) {EXPECT_TRUE(checkData());
}INSTANTIATE_TEST_CASE_P(TestBisValuesCombine, CombineTest, Combine(Bool(), Values(0, 1, 2, 3, 4)));
如何我们便可以衍生出8组测试。我们看下部分测试结果输出
[----------] 8 tests from TestBisValuesCombine/CombineTest
......
[ RUN ] TestBisValuesCombine/CombineTest.Test/6
[ OK ] TestBisValuesCombine/CombineTest.Test/6 (0 ms)
[ RUN ] TestBisValuesCombine/CombineTest.Test/7
../samples/sample11_unittest.cc:175: Failure
Value of: checkData()Actual: false
Expected: true
[ FAILED ] TestBisValuesCombine/CombineTest.Test/7, where GetParam() = (true, 3) (1 ms)
[----------] 8 tests from TestBisValuesCombine/CombineTest (2 ms total)
上例中TestBisValuesCombine/CombineTest是最终的测试用例名,Test/6和Test/7是其下两个测试特例名。
我们最后把参数生成函数罗列下
Range(begin, end[, step]) | Yields values {begin, begin+step, begin+step+step, ...}. The values do not include end. step defaults to 1. |
Values(v1, v2, ..., vN) | Yields values {v1, v2, ..., vN}. |
ValuesIn(container) and ValuesIn(begin, end) | Yields values from a C-style array, an STL-style container, or an iterator range [begin, end). container, begin, and end can be expressions whose values are determined at run time. |
Bool() | Yields sequence {false, true}. |
Combine(g1, g2, ..., gN) | Yields all combinations (the Cartesian product for the math savvy) of the values generated by the N generators. This is only available if your system provides the <tr1/tuple> header. If you are sure your system does, and Google Test disagrees, you can override it by defining GTEST_HAS_TR1_TUPLE=1. See comments in include/gtest/internal/gtest-port.h for more information. |
参数自动填充机制解析
该机制和之前介绍的各种技术都不同,所以我们还要从函数注册、自动调用等基础方面去解析。
注册
之前的博文中,我们都是使用TEST宏。它帮我们完成了测试类的注册和测试实体的组织(详见《Google Test(GTest)使用方法和源码解析——自动调度机制分析》)。本节我们使用的都是TEST_P宏,其实现方式和TEST宏有类似的地方
- 都定义了一个测试类
- 都声明了一个虚方法——TestBody
- 都将赋值符设置为私有
- 都在末尾定了TestBody函数体的一部分,要求用户去填充测试实体
# define TEST_P(test_case_name, test_name) \class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \: public test_case_name { \public: \GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \virtual void TestBody(); \private:GTEST_DISALLOW_COPY_AND_ASSIGN_(\GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \
......void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
不同的地方便是TestBody方法由私有变成公有,还有就是类的注册
private: \static int AddToRegistry() { \::testing::UnitTest::GetInstance()->parameterized_test_registry(). \GetTestCasePatternHolder<test_case_name>(\#test_case_name, \::testing::internal::CodeLocation(\__FILE__, __LINE__))->AddTestPattern(\#test_case_name, \#test_name, \new ::testing::internal::TestMetaFactory< \GTEST_TEST_CLASS_NAME_(\test_case_name, test_name)>()); \return 0; \} \static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \
......}; \int GTEST_TEST_CLASS_NAME_(test_case_name, \test_name)::gtest_registering_dummy_ = \GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
TEST_P宏暴露出来的静态变量gtest_registering_dummy_明显只是一个辅助,它的真正目的只是为了让其可以在main函数之前初始化,并在初始化函数中完成类的注册。而注册函数也是实现在TEST_P定义的类的内部,但是是个静态成员函数。
注册过程中,单例UnitTest调用了parameterized_test_registry方法返回一个ParameterizedTestCaseRegistry对象引用。它是参数自动填充机制类(之后称Parameterized类)的注册场所。其内部变量test_case_infos_保存了所有Parameterized类对象的指针
private:typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer;TestCaseInfoContainer test_case_infos_;
该类还暴露了一个非常重要的方法GetTestCasePatternHolder,它用于返回一个测试用例对象指针
template <class TestCase>ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(const char* test_case_name,CodeLocation code_location) {ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL;for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();it != test_case_infos_.end(); ++it) {if ((*it)->GetTestCaseName() == test_case_name) {if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) {// Complain about incorrect usage of Google Test facilities// and terminate the program since we cannot guaranty correct// test case setup and tear-down in this case.ReportInvalidTestCaseType(test_case_name, code_location);posix::Abort();} else {// At this point we are sure that the object we found is of the same// type we are looking for, so we downcast it to that type// without further checks.typed_test_info = CheckedDowncastToActualType<ParameterizedTestCaseInfo<TestCase> >(*it);}break;}}if (typed_test_info == NULL) {typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name, code_location);test_case_infos_.push_back(typed_test_info);}return typed_test_info;}
该方法是个模板方法,模板是我们通过TEST_P传入的测试用例类。它通过我们传入的测试用例名和代码所在行数等信息,创建一个或者返回一个已存在的ParameterizedTestCaseInfo<T>*类型的数据,其指向了符合以上信息的测试用例对象。这个对象内部保存了一系列测试特例类指针
typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
TestInfoContainer tests_;
TEST_P宏通过该对象,调用AddTestPattern方法向测试用例对象新增当前测试特例对象
void AddTestPattern(const char* test_case_name,const char* test_base_name,TestMetaFactoryBase<ParamType>* meta_factory) {tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name,test_base_name,meta_factory)));}
这个过程和TEST宏的思路基本一致,不同的是它引入了很多模板。但是需要注意的是,这并不是向框架的可执行队列中插入测试用例或者测试测试特例信息的地方,这只是中间临时保存的过程。
我们再看看INSTANTIATE_TEST_CASE_P的实现,它首先定义了一个返回参数生成器的函数
# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, ...) \::testing::internal::ParamGenerator<test_case_name::ParamType> \gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \
这个函数非常重要,之后我们就靠它生成参数。
然后定义了一个返回参数名称的函数
::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \const ::testing::TestParamInfo<test_case_name::ParamType>& info) { \return ::testing::internal::GetParamNameGen<test_case_name::ParamType> \(__VA_ARGS__)(info); \} \
最后它定义了一个全局傀儡变量,在其初始化时,抢在main函数执行之前注册相关信息
int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \::testing::UnitTest::GetInstance()->parameterized_test_registry(). \GetTestCasePatternHolder<test_case_name>(\#test_case_name, \::testing::internal::CodeLocation(\__FILE__, __LINE__))->AddTestCaseInstantiation(\#prefix, \>est_##prefix##test_case_name##_EvalGenerator_, \>est_##prefix##test_case_name##_EvalGenerateName_, \__FILE__, __LINE__)
可见它也是通过测试用例的类名获取我们之前通过TEST_P创建的测试用例类对象,然后调用AddTestCaseInstantiation方法,传入参数生成函数指针(参数生成器)和参数名生成函数指针。通过这些信息,将新建一个定制化(Instantiation)的测试对象——Instantiationinfo。AddTestCaseInstantiation将该定制化测试对象保存到template <class TestCase> class ParameterizedTestCaseInfo类里成员变量中。
// INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information// about a generator.int AddTestCaseInstantiation(const string& instantiation_name,GeneratorCreationFunc* func,ParamNameGeneratorFunc* name_func,const char* file,int line) {instantiations_.push_back(InstantiationInfo(instantiation_name, func, name_func, file, line));return 0; // Return value used only to run this method in namespace scope.}typedef ::std::vector<InstantiationInfo> InstantiationContainer;InstantiationContainer instantiations_;
至此,我们把所有在main函数之前执行的操作给看完了。但是仍然没有发现GTest框架是如何将这些临时信息保存到执行队列中的,更没有看到调度的代码。
归类及再注册
最后我们在main函数的testing::InitGoogleTest(&argc, argv);中发现如下代码
GetUnitTestImpl()->PostFlagParsingInit();
PostFlagParsingInit最终将会调用到
void UnitTestImpl::RegisterParameterizedTests() {
#if GTEST_HAS_PARAM_TESTif (!parameterized_tests_registered_) {parameterized_test_registry_.RegisterTests();parameterized_tests_registered_ = true;}
#endif
}
parameterized_test_registry_就是之前在TEST_P和INSTANTIATE_TEST_CASE_P宏中使用到的::testing::UnitTest::GetInstance()->parameterized_test_registry()的返回值,我们看看RegisterTests()里干了什么
void RegisterTests() {for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();it != test_case_infos_.end(); ++it) {(*it)->RegisterTests();}}
它遍历了所有通过TEST_P保存的测试用例对象(ParameterizedTestCaseInfo<T>),然后逐个调用其RegisterTests方法
virtual void RegisterTests() {for (typename TestInfoContainer::iterator test_it = tests_.begin();test_it != tests_.end(); ++test_it) {linked_ptr<TestInfo> test_info = *test_it;
一开始它枚举了所有之前通过TEST_P保存的测试特例对象,之后都会对该对象进行操作
for (typename InstantiationContainer::iterator gen_it =instantiations_.begin(); gen_it != instantiations_.end();++gen_it) {const string& instantiation_name = gen_it->name;ParamGenerator<ParamType> generator((*gen_it->generator)());ParamNameGeneratorFunc* name_func = gen_it->name_func;const char* file = gen_it->file;int line = gen_it->line;string test_case_name;if ( !instantiation_name.empty() )test_case_name = instantiation_name + "/";test_case_name += test_info->test_case_base_name;size_t i = 0;std::set<std::string> test_param_names;
然后枚举该测试用例中所有通过INSTANTIATE_TEST_CASE_P宏保存的定制化测试对象,并准备好相关数据供之后使用。
for (typename ParamGenerator<ParamType>::iterator param_it =generator.begin();param_it != generator.end(); ++param_it, ++i) {Message test_name_stream;std::string param_name = name_func(TestParamInfo<ParamType>(*param_it, i));GTEST_CHECK_(IsValidParamName(param_name))<< "Parameterized test name '" << param_name<< "' is invalid, in " << file<< " line " << line << std::endl;GTEST_CHECK_(test_param_names.count(param_name) == 0)<< "Duplicate parameterized test name '" << param_name<< "', in " << file << " line " << line << std::endl;test_param_names.insert(param_name);test_name_stream << test_info->test_base_name << "/" << param_name;
这段代码遍历参数生成器,并使用参数名生成器把所有参数转换成一个string类型数据,插入到待输出的内容中
调度
RegisterTests函数最后将调用如下过程
MakeAndRegisterTestInfo(test_case_name.c_str(),test_name_stream.GetString().c_str(),NULL, // No type parameter.PrintToString(*param_it).c_str(),code_location_,GetTestCaseTypeId(),TestCase::SetUpTestCase,TestCase::TearDownTestCase,test_info->test_meta_factory->CreateTestFactory(*param_it));} // for param_it} // for gen_it} // for test_it} // RegisterTests
MakeAndRegisterTestInfo函数在之前的博文中做过分析,它将所有测试用例和测试特例保存到GTest框架的可执行队列中,从而完成调度前的所有准备工作。至于调度及MakeAndRegisterTestInfo的细节可以参见《Google Test(GTest)使用方法和源码解析——自动调度机制分析》。
说了这么多理论,我们以之前的例子为例
TEST_P(CheckBisEven, Test) {EXPECT_TRUE(Even(GetParam()));
}int values[] = {0, 1};
INSTANTIATE_TEST_CASE_P(TestBisValuesIn, CheckBisEven, ValuesIn(values));
INSTANTIATE_TEST_CASE_P(TestBisValues, CheckBisEven, Values(11, 12, 13, 14));
第1行将新建名为CheckBisEven的测试用例。并在该测试用例下新建并保存一个名为CheckBisEven_Test_Test的测试特例。
第6、7行将在CheckBisEven测试用例下新增两个定制化测试对象。至此main函数之前的数据保存工作完毕,但是数据保存在一个临时区域。
testing::InitGoogleTest方法遍历所有的测试用例对象。针对每个测试用例,又遍历其测试特例对象。对每个测试特例对象,再遍历这个测试用例中保存的定制化测试对象(上例中有两个定制化测试对象)。使用定制化测试对象生成参数,通过MakeAndRegisterTestInfo方法将重新组织关系的测试用例和被参数化的测试特例保存到GTest的可执行队列中。从而在之后被框架自动调度起来。
为了区分之前的测试特例,MakeAndRegisterTestInfo使用了新的测试用例和测试特例名。测试用例名的生成规则是(INSTANTIATE_TEST_CASE_P宏的第一个参数/INSTANTIATE_TEST_CASE_P的第二个参数)——上例是TestBisValuesIn/CheckBisEven和TestBisValues/CheckBisEven,测试特例名的生成规则是(TEST_P的第二个参数/当前参数值)——上例是Test/0、Test/1、Test/11、Test/12、Test/13、Test/14。于是上例就会生成两个测试用例,分别有2个和4个测试特例。每个参数是一个特例。这些才是框架执行的测试对象。
参数传递
通过上面分析,我们可以得知,TEST_P定义的测试类,可能分属于两个不同的测试的特例(上例就分属于测试用例TestBisValuesIn/CheckBisEven和TestBisValues/CheckBisEven)。于是我们在MakeAndRegisterTestInfo函数调用中看到
test_info->test_meta_factory->CreateTestFactory(*param_it)
这行代码是通过类厂,新建了一个特例对象。
我们将重点放到类厂的实现,这将有助于我们发现参数是怎么传递的。
测试用例信息的类中保存了一系列TestInfo对象,每个TestInfo对象都有一个用于“生成携带参数的对象”的类厂——test_meta_factory
template <class TestCase>
class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
.......typedef typename TestCase::ParamType ParamType;
.......struct TestInfo {TestInfo(const char* a_test_case_base_name,const char* a_test_base_name,TestMetaFactoryBase<ParamType>* a_test_meta_factory) :......,test_meta_factory(a_test_meta_factory) {}.......const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;};
......
}
注意下ParamType,它是我们传入的模板类的一个属性,即测试用例类的属性,但是我们好像没有定义过它。其实我们是通过继承template <typename T> class WithParamInterface类来设置该属性的
template <typename T>
class WithParamInterface {public:typedef T ParamType;
回顾下我们测试用例类的设计
class CheckBisEven :public TestClass,public ::testing::WithParamInterface<int>
{
};
可见该用例的ParamType就是我们指定的int。框架在不知道我们指定了哪个类型的情况下,选择了一个替代符实现之后逻辑的,这在模板类设计中经常见到。
我们再回到类厂的实现上来。test_meta_factory是在TEST_P宏中使用下列方法新建的
new ::testing::internal::TestMetaFactory< GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()
TestMetaFactory的定义如下
template <class TestCase>
class TestMetaFactory: public TestMetaFactoryBase<typename TestCase::ParamType> {public:typedef typename TestCase::ParamType ParamType;
......
};
它是个模板类,继承于另一个模板类TestMetaFactoryBase,TestMetaFactoryBase的模板是TestMetaFactory模板的ParamType属性,对应于上例就是int。TestMetaFactoryBase类是个接口类,没什么好说的。
在MakeAndRegisterTestInfo注册测试特例时,使用了该特例的类厂对象调用CreateTestFactory方法
virtual TestFactoryBase* CreateTestFactory(ParamType parameter) {return new ParameterizedTestFactory<TestCase>(parameter);}
它又新建了一个模板类对象指针
template <class TestClass>
class ParameterizedTestFactory : public TestFactoryBase {public:typedef typename TestClass::ParamType ParamType;explicit ParameterizedTestFactory(ParamType parameter) :parameter_(parameter) {}
.....private:const ParamType parameter_;GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory);
};
新建的类厂对象最终会保存到TestInfo中,并在测试用例执行前被调用,从而生成对应的测试特例对象。这段逻辑在《Google Test(GTest)使用方法和源码解析——自动调度机制分析》有过分析
void TestInfo::Run() {
.......Test* const test = internal::HandleExceptionsInMethodIfSupported(factory_, &internal::TestFactoryBase::CreateTest,"the test fixture's constructor");
.......
}
我们再将关注的重点放到ParameterizedTestFactory 的CreateTest方法,它先通过模板类的SetParam方法设置了参数,然后新建并返回了一个模板类对象。
virtual Test* CreateTest() {TestClass::SetParam(¶meter_);return new TestClass();}
我们的测试用例类怎么有SetParam方法?其实这也是在我们继承的WithParamInterface类中实现的
template <typename T>
class WithParamInterface {public:typedef T ParamType;virtual ~WithParamInterface() {}// The current parameter value. Is also available in the test fixture's// constructor. This member function is non-static, even though it only// references static data, to reduce the opportunity for incorrect uses// like writing 'WithParamInterface<bool>::GetParam()' for a test that// uses a fixture whose parameter type is int.const ParamType& GetParam() const {GTEST_CHECK_(parameter_ != NULL)<< "GetParam() can only be called inside a value-parameterized test "<< "-- did you intend to write TEST_P instead of TEST_F?";return *parameter_;}private:// Sets parameter value. The caller is responsible for making sure the value// remains alive and unchanged throughout the current test.static void SetParam(const ParamType* parameter) {parameter_ = parameter;}// Static value used for accessing parameter during a test lifetime.static const ParamType* parameter_;// TestClass must be a subclass of WithParamInterface<T> and Test.template <class TestClass> friend class internal::ParameterizedTestFactory;
};
该类保存了一个静态的全局变量parameter_,它保存了参数的指针。并通过SetParam和GetParam方法设置这个全局参数。
于是参数传递的过程就很明确了:新建TestInfo前,在一个全局区域保存参数,然后通过GetParam方法获取该全局变量,从而实现参数的传递。
其实这儿还有个非常有意思的技术点,就是参数生成器的实现。由于它不是这个框架的重点,而且相关内容也不少,我就不打算在这儿分析了,大家有兴趣可以自己看看。
相关文章:

Linux 指令篇:文件系统--fstab
Linux 指令篇:文件系统-----FSTAB指令:FSTAB使用权限 : 超级使用者 使用方式 : 使用编辑器来修改 /etc/fstab (eg. vi /etc/fstab) 说明 : 存放档案系统与目录结构对应资料的档案 fstab 栏位说明: 第一栏(fs_spec): 实际的 device…

跨平台抓包软件,可以替代Fiddler
2019独角兽企业重金招聘Python工程师标准>>> Zed Attack Proxy (ZAP) 是个强大的跨平台的抓包工具,可以用来替代windows下的Fiddler https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project https://github.com/zaproxy/zaproxy/wiki/Download…
集五福,我用Python
所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项,还有更多福利赠送作者 | Crossin先生编辑 | Jane来源 | Crossin的编程教室(ID:crossincode)【导读】你的五福集齐了吗?作为一名技术人,我们是不是可以…

Google Test(GTest)使用方法和源码解析——模板类测试技术分析和应用
写C难免会遇到模板问题,如果要针对一个模板类进行测试,似乎之前博文中介绍的方式只能傻乎乎的一个一个特化类型后再进行测试。其实GTest提供了两种测试模板类的方法,本文我们将介绍方法的使用,并分析其实现原理。(转载…

IT人才职场受宠
面对就业压力的日益增大,就业难,工资水平低等问题困扰着所有的大学生。然而,IT业的迅猛发展却造成了数以万计的网络设计、运行、维护的网络工程师需求的空缺,巨大的人才缺口使得IT业“全线告急”,这也促使更多的研究人…

引用头文件#include queue出错
为什么80%的码农都做不了架构师?>>> 在工程头文件中引用头文件 #include <queue> 莫名奇妙出错,其原因很可能是由于头文件引用问题。 include/c/4.7.1/bits/stl_vector.h:1308:40: error: expected unqualified-id before ‘(’ token…

ZOJ1002 Fire Net(非递归版)
以前用递归的回溯搜索思路做过一次,参见ZOJ1002 Fire Net(递归版),今天想着用非递归的方法试试看,呵呵,比我想象中要难啊,主要还是堆栈里究竟放什么,这一点上思路一直没理清。因此用了整整一天的时间&#…
“数学不行,干啥也不行”骨灰级程序员:其实你们都是瞎努力
编程圈一直都流传着一个段子:一流程序员靠数学,二流程序员靠算法,末端程序员靠百度,低端看高端就是黑魔法。懂的人其实都知道,这不是段子,其实就是程序员的真实写照。想一想,我们日常学习、求职…

Google Test(GTest)使用方法和源码解析——死亡测试技术分析和应用
死亡测试是为了判断一段逻辑是否会导致进程退出而设计的。这种场景并不常见,但是GTest依然为我们设计了这个功能。我们先看下其应用实例。(转载请指明出于breaksoftware的csdn博客) 死亡测试技术应用 我们可以使用TEST声明并注册一个简单的测…
java学习笔记11--Annotation
java学习笔记11--Annotation Annotation:在JDK1.5之后增加的一个新特性,这种特性被称为元数据特性,在JDK1.5之后称为注释,即:使用注释的方式加入一些程序的信息。 java.lang.annotation Annotation接口是所有的Annotat…

GoogleLog(GLog)源码分析
GLog是Google开发的一套日志输出框架。由于其具有功能强大、方便使用等特性,它被众多开源项目使用。本文将通过分析其源码,解析Glog实现的过程。 该框架的源码在https://github.com/google/glog上可以获取到。本文将以目前最新的0.3.3版本源码为范例进行…

Ajax Toolkit 控件学习系列(13) ——FilteredTextBoxExtender 控制输入
这个控件的作用是对TextBox所要输入的内容进行过滤控制。按照自己需要过滤,可以自定义,再或者使用定义好的方式。看效果。效果不是很突出,说明下,就是只能输入大写字母和数字。因为加了限制,但是具体有什么高深的应用呢…
Uber最新开源Manifold,助力机器学习开发者的可视化与调试需求
所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项,还有更多福利赠送作者 | Lezhi Li译者 | 凯隐编辑 | Jane出品 | AI科技大本营(ID:rgznai100)【导语】2019 年 1 月,Uber 推出了 Manifold,一款与模型无…

jQuery对象和DOM对象使用说明
1.jQuery对象和DOM对象第一次学习jQuery,经常分辨不清哪些是jQuery对象,哪些是 DOM对象,因此需要重点了解jQuery对象和DOM对象以及它们之间的关系.DOM对象,即是我们用传统的方法(javascript)获得的对象,jQuery对象即是用jQuery类库…

[WPF疑难]避免窗口最大化时遮盖任务栏
[WPF疑难]避免窗口最大化时遮盖任务栏 周银辉 WPF窗口最大化时有个很不好的现象是:如果窗口的WindowStyle被直接或间接地设置为None后(比如很多情况下你会覆盖默认的窗体样式,即不采用Windows默认的边框和最大化最等按钮,来打造个…

Google Mock(Gmock)简单使用和源码分析——简单使用
初识Gmock是之前分析GTest源码时,它的源码和GTest源码在同一个代码仓库中(https://github.com/google/googletest)。本文我将以目前最新的Gmock1.7版本为范例,分析其实现原理。(转载请指明出于breaksoftware的csdn博客…
浪潮刘军:为什么说计算力是AI时代“免费的午餐”?
出品 | AI科技大本营(ID:rgznai100)产业AI、元脑生态是浪潮集团2019年度的两大关键词。作为一家以计算力为核心生产力的企业,浪潮还一直强调人工智能计算是未来最重要的计算力,而无论产业AI、元脑生态都构筑于计算的基础设施之上。…

Journey源码分析四:url路由
2019独角兽企业重金招聘Python工程师标准>>> 在入口函数main()的default分支中,对路由进行了注册,现在分析下。 ##main()中路由注册相关代码 源码: httpRouter : httptreemux.New() // Blog and pages as http server.InitializeBlog(httpRou…
“天河二号”总工程师杜云飞谈星光超算应用平台设计
整理 | 夕颜出品 | AI科技大本营(ID:rgznai100)【导读】12 月 21-22 日,OpenI/O 启智开发者大会在深圳召开。在大会上, 国家超级计算广州中心总工程师、“天河二号”总工程师杜云飞发表了题为《星光超算应用平台》的主题报告&…

Google Mock(Gmock)简单使用和源码分析——源码分析
源码分析 通过《Google Mock(Gmock)简单使用和源码分析——简单使用》中的例子,我们发现被mock的相关方法在mock类中已经被重新实现了,否则它们也不会按照我们的期待的行为执行。我们通过阅读源码,来分析整个过程的实现逻辑。(转载…

远程控制软件VNC教程和对内网机器控制的实现
网络遥控技术是指由一部计算机(主控端)去控制另一部计算机(被控端),而且当主控端在控制端时,就如同用户亲自坐在被控端前操作一样,可以执行被控端的应用程序,及使用被控端的系统资源…

GRUB2相关概念
GNU GRUB,简称“GRUB”,是一个来自GNU项目的启动引导程序。GRUB是多启动规范的实现,它允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统。GRUB可用于选择操作系统分区上的不同内核,…
朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型
做Linux网络开发,一般绕不开标题中几种网络编程模型。网上已有很多写的不错的分析文章,它们的基本论点是差不多的。但是我觉得他们讲的还不够详细,在一些关键论点上缺乏数据支持。所以我决定好好研究这几个模型。(转载请指明出于b…
支付宝账单出来后,除了总消费,你看到你的学习支出了吗?
当支付宝的2019年年度账单出来的时候很多人的第一反应就是我怎么花了这么多钱不少人都有这样的困惑忙忙碌碌一年到头,到底得到了什么?而这一切又和自己最开始的规划一样吗?其实从账单上可以看出你在2019年花费了哪些大头居家生活、穿衣打扮还…

体验Windows 7的Superbar
随着PDC 2008上Windows 7 M3 6801的发布,这个微软的下一代操作系统也渐渐浮出了水面。对于我们这些普通的PC用户而言,Windows 7相对于Windows Vista最显而易见的改变,无疑就是著名的Superbar任务栏了。说起它相比于原来的任务栏变化ÿ…

Linux 安装图形界面及远程连接
#可查询哪些组件是否已经安装(可用来对照组件名称) yum grouplistyum groupinstall X Window System -y #安装GNOME桌面环境 yum groupinstall GNOME Desktop Environment -y #安装KDE桌面环境 yum groupinstall KDE (K Desktop Environment)卸载 卸载GNOME桌面环境…
朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型
在《朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型》中我们分析了朴素模型的一个缺陷——一次只能处理一个连接。本文介绍的Select模型则可以解决这个问题。(转载请指明出于breaksoftware的csdn博客) 和朴素模型一样,我们首先…
微软开源NAS算法Petridish,提高神经网络迁移能力
作者 | Jesus Rodriguez译者 | Rachel编辑 | 夕颜出品 | AI科技大本营(ID:rgznai100) 【导读】神经架构搜索(Neural architecture search, NAS)是深度学习中最热门的趋势之一。NAS方法针对特定问题和数据…

[转]g++ 编译多个相关文件
三个文件,一个头文件,两个程序文件 /*d.h */#include <iostream>usingnamespacestd; classDataset { public: intgetdata(); }; /*d.cpp */#include "d.h"intDataset::getdata() { return1231; } /*out.cpp */#include <ios…

POJ--2391--Ombrophobic Bovines【分割点+Floyd+Dinic优化+二分法答案】最大网络流量
联系:http://poj.org/problem?id2391 题意:有f个草场,每一个草场当前有一定数目的牛在吃草,下雨时它能够让一定数量的牛在这里避雨,f个草场间有m条路连接,每头牛通过一条路从一点到还有一点有一定的时间花…