Google Test
来源:互联网 发布:sql not like 编辑:程序博客网 时间:2024/05/01 19:12
Google Test
主页:http://code.google.com/p/googletest/
教程:http://code.google.com/p/googletest/wiki/V1_6_Documentation
断言
ASSERT_* 一旦失败立即退出当前函数,EXPECT_*还能继续下去。
基础断言
致命断言
非致命断言
验证条件
ASSERT_TRUE(condition);
EXPECT_TRUE(condition);
condition为真
ASSERT_FALSE(condition);
EXPECT_FALSE(condition);
condition为假
数据比较
致命断言
非致命断言
验证条件
ASSERT_EQ(期望值, 实际值);
EXPECT_EQ(期望值,实际值);
期望值 == 实际值
ASSERT_NE(val1, val2);
EXPECT_NE(val1, val2);
val1 != val2
ASSERT_LT(val1, val2);
EXPECT_LT(val1, val2);
val1 < val2
ASSERT_LE(val1, val2);
EXPECT_LE(val1, val2);
val1 <= val2
ASSERT_GT(val1, val2);
EXPECT_GT(val1, val2);
val1 > val2
ASSERT_GE(val1, val2);
EXPECT_GE(val1, val2);
val1 >= val2
字符串比较
针对C形式的字符串,即char*或wchar_t*对比测试:
致命断言
非致命断言
验证条件
ASSERT_STREQ(expected_str, actual_str);
EXPECT_STREQ(expected_str, actual_str);
两个C字符串有相同的内容
ASSERT_STRNE(str1, str2);
EXPECT_STRNE(str1, str2);
两个C字符串有不同的内容
ASSERT_STRCASEEQ(expected_str, actual_str);
EXPECT_STRCASEEQ(expected_str, actual_str);
两个C字符串有相同的内容,忽略大小写
ASSERT_STRCASENE(str1, str2);
EXPECT_STRCASENE(str1, str2);
两个C字符串有不同的内容,忽略大小写
浮点数比较
致命断言
非致命断言
验证项目
ASSERT_FLOAT_EQ(expected, actual);
EXPECT_FLOAT_EQ(expected, actual);
两个float近似相等
ASSERT_DOUBLE_EQ(expected, actual);
EXPECT_DOUBLE_EQ(expected, actual);
两个double近似相等
下面的断言允许你自己选择可接受的误差范围:
致命断言
非致命断言
验证项目
ASSERT_NEAR(val1, val2, abs_error);
EXPECT_NEAR(val1, val2, abs_error);
val1和val2之间的差距没有超出abs_error指定的绝对误差范围
异常断言
致命断言
非致命断言
验证项目
ASSERT_THROW(statement, exception_type);
EXPECT_THROW(statement, exception_type);
statement 抛出类型为exception_type的异常
ASSERT_ANY_THROW(statement);
EXPECT_ANY_THROW(statement);
statement 抛出一个异常
ASSERT_NO_THROW(statement);
EXPECT_NO_THROW(statement);
statement 不会抛出异常
ASSERT_THROW(Foo(5), bar_exception);
EXPECT_NO_THROW({
int n = 5;
Bar(&n);
});
谓语断言
用户可以根据需求自定义断言:
致命断言
非致命断言
验证项目
ASSERT_PRED1(pred1, val1);
EXPECT_PRED1(pred1, val1);
pred1(val1) 返回 true
ASSERT_PRED2(pred2, val1, val2);
EXPECT_PRED2(pred2, val1, val2);
pred2(val1, val2) 返回 true
…
…
…
最多支持5个参数
//判断是否互质
bool MutuallyPrime(int m, int n) { ... }
const int a = 3;
const int b = 4;
const int c = 10;
EXPECT_PRED2(MutuallyPrime, a, b);
EXPECT_PRED2(MutuallyPrime, b, c);
MutuallyPrime(a, b) is true,
where
a is 3
b is 4
MutuallyPrime(b, c) is false,
where
b is 4
c is 10
用户可以自定义谓语断言的输出流:
致命断言
非致命断言
验证项目
ASSERT_PRED_FORMAT1(pred_format1, val1);
EXPECT_PRED_FORMAT1(pred_format1, val1);
pred_format1(val1) 成功
ASSERT_PRED_FORMAT2(pred_format2, val1, val2);
EXPECT_PRED_FORMAT2(pred_format2, val1, val2);
pred_format2(val1, val2) 成功
…
…
…
pred_format采用的是如下函数形式:
/**
* @brief PredicateFormattern pred_format函数
*
* @param expr1 val1的名称
* @param expr2 val2的名称
* @param exprn valn的名称
* @param val1 参数val1
* @param val2 参数val2
* @param valn 参数valn
*
* @return 返回类型为test::AssertionResult
*/
testing::AssertionResult
PredicateFormattern(
const char* expr1, const char* expr2, ... const char* exprn,
T1 val1, T2 val2, ... Tn valn);
test::AssertionResult的返回类型有两个值如下:
namespace testing {
//成功
AssertionResult AssertionSuccess();
//失败
AssertionResult AssertionFailure(const Message& msg);
}
作为例子,我们来改善前面使用EXPECT_PRED2()的那个例子里的失败信息:
//求最小公约数
int SmallestPrimeCommonDivisor(int m, int n) { ... }
//自己声明pre_format()的例子
testing::AssertionResult AssertMutuallyPrime(const char* m_expr, const char* n_expr, int m, int n)
//前两个是参数名称:a,b 后面两个是a和b的传入值
{
//判断互质
if (MutuallyPrime(m, n))
return testing::AssertionSuccess();//返回成功
//定义了一个testing::Message来打印出错信息
testing::Message msg;
//自己定义的出错格式
msg << m_expr << " and " << n_expr << " (" << m << " and " << n
<< ") are not mutually prime, " << "as they have a common divisor "
<< SmallestPrimeCommonDivisor(m, n);
return testing::AssertionFailure(msg);//返回失败,并传入出错信息作为参数
}
//调用方法
EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c);
打印结果:
b and c (4 and 10) are not mutually prime, as they have a common divisor 2
死亡测试
任何检查程序是否按预期退出的测试:
致命断言
非致命断言
验证项目
ASSERT_DEATH(statement, regex);
EXPECT_DEATH(statement, regex);
statement 以预期的错误崩溃
ASSERT_EXIT(statement, predicate, regex);
EXPECT_EXIT(statement, predicate, regex);
statement 以预期的错误退出并且错误码匹配predicate
statement是一个可能造成进程死亡的程序段。
regex是一个正则表达式用于匹配stderr的输出内容。
predicate是一个bool类型的表达式,可以是函数,也可以是一个bool值,也可以是一个bool表达式。
· 如: bool f(), true或 a==b
Google Test自带了两种判断进程结束的断言函数,返回值为bool,只接受一个int参数:
testing::ExitedWithCode(exit_code);
testing::KilledBySignal(signal_number);
TEST(MyDeathTest, Foo) {
// This death test uses a compound statement.
ASSERT_DEATH({ int n = 5; Foo(&n); }, "Error on line .* of Foo()");
}
TEST(MyDeathTest, NormalExit) {
//testing::ExitedWithCode(exit_code)返回码必须一致才算测试成功
EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success");
}
TEST(MyDeathTest, KillMyself) {
//testing::KilledBySignal(signal_number)杀死进程的信号必须一致才算测试成功
EXPECT_EXIT(KillMyself(), testing::KilledBySignal(SIGKILL), "Sending myself unblockable signal");
}
检验项目:
· Foo(5)使进程死亡并得到指定的信息。
· NormalExit()使进程打印”Success”到stderr并且退出码为0。
· KillMyself()使用信号 SIGKILL杀死进程。
Death断言只适用于Linux
Windows的HRESULT断言
这些断言用于测试HRESULT成功或失败:
致命断言
非致命断言
验证项目
ASSERT_HRESULT_SUCCEEDED(expression);
EXPECT_HRESULT_SUCCEEDED(expression);
expression 返回成功的 HRESULT
ASSERT_HRESULT_FAILED(expression);
EXPECT_HRESULT_FAILED(expression);
expression 返回失败的 HRESULT
HRESULT断言只适用于windows
测试框架
测试用例main函数,头文件只要包含gtest/gtest.h即可:
gtest_main.cc
#include <iostream>
#include "gtest/gtest.h"
GTEST_API_ int main(int argc, char **argv) {
std::cout << "Running main() from gtest_main.cc\n";
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
TEST
简单测试用例
TEST(test_case_name, test_name){}
第一个参数是测试用例名称,第二个参数是一个该用例中的测试名称。
#include "gtest/gtest.h"
TEST(TestFactorial, ZeroInput) {
EXPECT_EQ(1, Factorial(0));
}
TEST(TestFactorial, OtherInput) {
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
TEST_F
TEST_F(test_fixture, test_name){}
第一个参数测试夹具名称,第二个参数测试该测试夹具中的一个测试名称。
#include "gtest/gtest.h"
// 用于测试Foo类的测试夹具,继承类testing::Test
class FooTest : public ::testing::Test{
protected:
// 在该测试用例的第一个测试开始前调用
static void SetUpTestCase() {
shared_resource_ =new ...;
}
// 在该测试用力的最后一个测试结束后调用
static void TearDownTestCase() {
delete shared_resource_;
shared_resource_ = NULL;
}
// 如果构造和析构还不能满足你的设置、清理工作
// 你还可以在下面两个函数里做这些
virtual void SetUp() { ... }
virtual void TearDown() { ... }
static T* shared_resource_;
};
T* FooTest::shared_resource_ = NULL;
TEST_F(FooTest, Test1) {
... you can refer to shared_resource here ...
}
TEST_F(FooTest, Test2) {
... you can refer to shared_resource here ...
}
TEST_P
值参数测试:
class FooTest : public ::testing::TestWithParam<const char*> {
// 通过调用函数GetParam()来获取模板类型的值
// 如:该类const char*模版类型的值
// 具体取值见下面的INSTANTIATE_TEST_CASE_P()
};
//如果想对已有的测试夹具加上值参数测试如下:
class BaseTest : public ::testing::Test {
...
};
class BarTest : public BaseTest,
public ::testing::WithParamInterface<const char*> {
...
};
TEST_P(CaseName, TestName){}
第一个参数测试夹具名称,第二个参数测试该测试夹具中的一个测试名称。
TEST_P(FooTest, DoesBlah) {
// 可以直接访问GetParam()函数来获取值参数
// 如:该类const char*模版类型的值
// 具体取值见下面的INSTANTIATE_TEST_CASE_P()
EXPECT_TRUE(foo.Blah(GetParam()));
...
}
TEST_P(FooTest, HasBlahBlah) {
...
}
INSTANTIATE_TEST_CASE_P(InstantiationName,
CaseName,
ValuesPred);
第一个参数实例化名称
第二个参数用例名称,比如本例应该是FooTest
第三个参数值表达式,值表达式在namespace testing中具体表达式如下表:
表达式
表达式含义
Range(begin, end[, step])
范围从begin开始,为begin+step, begin+step+step, …直到大于等于end结束,不包括end
Values(v1, v2, …, vN)
值枚举列表,取值为v1, v2, …, vN
ValuesIn(container) and ValuesIn(begin, end)
容器中取值,前者C模式,后者C++模式1)
Bool()
取值范围{false, true}
Combine(g1, g2, …, gN)
只有在系统提供了<tr1/tuple>2)头文件的时候才生效,配合tuple一起使用的
Combine也可以通过定义宏使其生效:
#define GTEST_HAS_TR1_TUPLE=1
用宏判断查看其是否生效:
#if GTEST_HAS_TR1_TUPLE
…
#endif
INSTANTIATE_TEST_CASE_P(InstantiationName,
FooTest,
::testing::Values("meeny", "miny", "moe"));
// 上面的GetParam()取值为"meeny", "miny", "moe"三个进行测试。
const char* pets[] = {"cat", "dog"};
INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest,
::testing::ValuesIn(pets));
// GetParam()从数组中进行取值
Combine详细用法
TYPED_TEST
模版类型测试:
//模版类型测试夹具
template <typename T>
class FooTest : public ::testing::Test {
public:
...
typedef std::list<T> List;
static T shared_;
T value_;
};
TYPED_TEST_CASE(CaseName, Types){}
第一个参数夹具即测试用例名称,第二个参数Types<>
//该测试模块是否可用
#if GTEST_HAS_TYPED_TEST
typedef ::testing::Types<char, int, unsigned int> MyTypes;
//初始化所有模版类型,必须要有的
//相当于测试了FooTest<char>,FooTest<int>和FooTest<unsigned int>三个类
TYPED_TEST_CASE(FooTest, MyTypes);
TYPED_TEST(CaseName, TestName){}
第一个参数夹具即测试用例名称,第二个参数该用例中的某一个测试名称
TYPED_TEST(FooTest, DoesBlah) {
//通过TypeParam来获得模版类型
TypeParam n = this->value_;
n += TestFixture::shared_;
//型别重命名
typename TestFixture::List values;
values.push_back(n);
...
}
#endif // GTEST_HAS_TYPED_TEST
TYPED_TEST_P
模版类型参数测试:
作用和TYPED_TEST非常相似,只不过写法有些不同。
template <typename T>
class FooTest : public ::testing::Test {
...
};
TYPED_TEST_CASE_P(FooTest);
只有一个参数测试夹具名称
TYPED_TEST_P(CaseName, TestName){}
第一个参数夹具即测试用例名称,第二个参数该用例中的某一个测试名称
TYPED_TEST_P(FooTest, DoesBlah) {
//同上也是通过TypeParam来获得模版类型
TypeParam n = 0;
...
}
TYPED_TEST_P(FooTest, HasPropertyA) { ... }
// 在实例化之前,告诉测试系统需要注册的用例和实际测试
// REGISTER_TYPED_TEST_CASE_P(CaseName, TestName1, TestName2, ...)
REGISTER_TYPED_TEST_CASE_P(FooTest,
DoesBlah, HasPropertyA);
// 实力化测试的模板类型
typedef ::testing::Types<char, int, unsigned int> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(InstantiateName, FooTest, MyTypes);
//如果只有一个类型的化也可以直接填
INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
个人感觉和TYPED_TEST测试作用很相似,只不过一个实例化类型在前一个在后。
FRIEND_TEST
友元测试,用来测试类的似有成员:
// foo.h
#include "gtest/gtest_prod.h"
// 待测试的类
class Foo {
...
private:
//定义友元测试的用例和测试
FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
int Bar(void* x);
};
// foo_test.cc
// 用例和测试名一致
TEST(FooTest, BarReturnsZeroOnNull) {
Foo foo;
// 可以使用私有成员变量了
EXPECT_EQ(0, foo.Bar(NULL));
}
夹具测试使用FRIEND_TEST的例子:
// foo.h
namespace my_namespace {
class Foo {
friend class FooTest;
FRIEND_TEST(FooTest, Bar);
FRIEND_TEST(FooTest, Baz);
...
definition of the class Foo
...
};
} // namespace my_namespace
// foo_test.cc
namespace my_namespace {
class FooTest : public ::testing::Test {
protected:
...
};
TEST_F(FooTest, Bar) { ... }
TEST_F(FooTest, Baz) { ... }
} // namespace my_namespace
Others
TestInfo
namespace testing {
class TestInfo {
public:
// 测试用例名称
const char* test_case_name() const;
// 测试名称
const char* name() const;
// 模板类型名称,主要用在TYPED_TEST和TYPED_TEST_P中
const char* type_param() const;
// 值参数名称,主要用在TEST_P中
const char* value_param() const;
};
}
TEST(CaseName, TestName)
{
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
printf("We are in test %s of test case %s.\n",
test_info->name(), test_info->test_case_name());
}
//打印结果:
//We are in test TestName of test case CaseName.
TestPartResult
断言的测试结果:
class TestPartResult {
public:
// explicit SUCCEED(), FAIL(), or ADD_FAILURE()).
enum Type {
kSuccess, //成功
kNonFatalFailure, //非致命断言失败,但是测试可以继续EXPECT_*断言失败时返回结果
kFatalFailure //致命断言失败,但是测试无法继续ASSERT_*断言失败时返回结果
};
// 返回测试断言结果
Type type() const;
// 断言所在文件名
const char* file_name() const;
// 断言所在行号
int line_number() const;
// 失败信息概要
const char* summary() const;
// 这部分测试关联的消息
const char* message() const;
// 返回测试是否成功,true表示成功
bool passed() const { return type_ == kSuccess; }
// 返回测试是否失败,true表示失败
bool failed() const { return type_ != kSuccess; }
// 返回测试是否为非致命断言失败
bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
// 返回测试是否为致命断言失败
bool fatally_failed() const { return type_ == kFatalFailure; }
};
// 都支持流输出
TEST(CaseName, TestName)
{
// 产生一个断言成功的TestPartResult
SUCCEED()<<"Something...";
// 产生一个非致命断言失败的TestPartResult
ADD_FAILURE()<<"Something...";
// 产生一个致命断言失败的TestPartResult
FAIL()<<"Something...";
}
Event Listeners
事件监听:
class EmptyTestEventListener : public TestEventListener {
public:
// Listener的被调用顺序一般会和下面顺序相差不多
// 测试程序启动时候调用,即RUN_ALL_TESTS()刚开始运行时候调用
virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
// 根据repeat次数多次调用,每次重复开始时调用,第二个参数传入第几次重复,从0开始。
virtual void OnTestIterationStart(const UnitTest& /*unit_test*/,
int /*iteration*/) {}
// SetUp()调用开始前调用
virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {}
// SetUp()调用结束时调用
virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
// 测试用例开始时调用
virtual void OnTestCaseStart(const TestCase& /*test_case*/) {}
// 测试用例中的单个测试开始时调用
virtual void OnTestStart(const TestInfo& /*test_info*/) {}
// 每个断言结束后调用,TestPartResult参数传入断言的结果,详见上节
virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {}
// 测试用例中的单个测试结束时调用
virtual void OnTestEnd(const TestInfo& /*test_info*/) {}
// 测试用例结束时调用
virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {}
// TearDown()调用开始前调用
virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {}
// TearDown()调用结束后调用
virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
// 根据repeat次数多次调用,每次重复结束时调用,第二个参数传入第几次重复,从0开始。
virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/,
int /*iteration*/) {}
// 测试程序结束时候调用,即RUN_ALL_TESTS()刚结束运行时候调用
virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
};
详细例子
运行选项
运行时可以通过–help, -h, -?或/?,来打印运行选项的帮助列表,所有的参数都是以两个-开头。
运行选项中所有的–gtest_foo都可以通过代码::testing::GTEST_FLAG(foo)来获取,并在编译器指定参数。
//--gest_print_time
int main(int argc, char** argv) {
// 关闭显示运行时间
// --gest_print_time
::testing::GTEST_FLAG(print_time) = false;
// --gest_list_tests
::testing::GTEST_FLAG(list_tests) = true;
// 这句代码将运行参数传入Google Test中
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
gtest_list_tests
不进行测试只显示所有测试列表:
TestCase1.
TestName1
TestName2
TestCase2.
TestName
gtest_filter
对测试进行过滤,只运行指定的测试:
* 代表任意数量任意字符
? 代表任意单个字符
: 代表或者的关系
- 代表去除该匹配测试
. 代表左边是测试用例名,右边是具体测试名称
例如:
· ./foo_test无参数,运行所有测试
· ./foo_test –gtest_filter=*运用*通配符,也是运行所有测试
· ./foo_test –gtest_filter=FooTest.*只运行所有在FooTest测试用例中的测试
· ./foo_test –gtest_filter=*Null*:*Constructor*只运行带有Null或者Constructor字符串的测试
· ./foo_test –gtest_filter=-*DeathTest.*运行除了带有DeathTest字符串的测试用例中的所有测试
· ./foo_test –gtest_filter=FooTest.*-FooTest.Bar运行在FooTest测试用例中除了FooTest.Bar的测试
gtest_also_run_disabled_tests
通常运行测试的时候,测试系统会自动禁止DISABLED_*为测试用例或测试名称的测试运行。
加上–gtest_also_run_disabled_tests运行选项之后,以DISABLED_开头的测试也会运行了。
如下:
// 默认是不运行的,加上运行选项则运行
TEST(FooTest, DISABLED_DoesAbc) { ... }
class DISABLED_BarTest : public ::testing::Test { ... };
TEST_F(DISABLED_BarTest, DoesXyz) { ... }
gtest_color
--gtest_color=(yes|no|auto)
Enable/disable colored output. The default is auto.
gtest_print_time
--gtest_print_time=0
Don't print the elapsed time of each test.
gtest_repeat
对测试进行重复运行,会把当前测试序号传递给TestEventListeners的OnTestIterationStart和OnTestIterationEnd第二个参数,详见Event Listeners:
命令
含义
foo_test --gtest_repeat=1000|重复运行1000次测试| | foo_test –gtest_repeat=-1
负数代表无限次数循环
foo_test --gtest_repeat=1000 --gtest_break_on_failure|重复运行1000,会在第一次测试失败的时候停止。--[[gtest_break_on_failure]]选项在调试器中非常的有用: 当测试失败停止以后会进入调试器,这样我们就可以查看当前的值和桟堆了。| | foo_test –gtest_repeat=1000 –gtest_filter=FooBar
只运行名为FooBar测试用例的测试1000次
gtest_shuffle
扰乱重复测试的顺序,将当前序号打乱传给OnTestIterationStart和OnTestIterationEnd,必须要和gtest_repeat选项同时使用。
gtest_random_seed
给gtest_shuffle设置SEED,0-9999之间,0代表取当前系统时间为SEED。
gtest_output
支持xml格式的输出:
命令
结果
./foo_test --gtest_output=xml|在当前目录输出文件 test_detail.xml| | ./foo_test –gtest_output=xml:dir/
在指定dir目录输出文件,和当前文件同名 foo_test.xml
$ ./foo_test –gtest_output=xml:dir/file.xml
在指定dir目录输出文件,指定文件名 file.xml
gtest_stream_result_to
定向输出流到指定服务器:
--gtest_stream_result_to=HOST:PORT
HOST服务器IP或域名,PORT服务器端口
gtest_death_test_style
--gtest_death_test_style=(fast|threadsafe)
Set the default death test style.
gtest_break_on_failure
--gtest_break_on_failure
Turn assertion failures into debugger break-points.
非常利于调试
gtest_throw_on_failure
--gtest_throw_on_failure
Turn assertion failures into C++ exceptions.
gtest_catch_exceptions
--gtest_catch_exceptions=0
Do not report exceptions as test failures. Instead, allow them
to crash the program or throw a pop-up (on Windows).
1)
ValuesIn(const T (&array)[N]) //数组
ValuesIn(const Container& container) //STL容器
ValuesIn(Iterator begin, Iterator end) //迭代器
- google test
- Google Test
- Google Test
- google test
- google test
- Google Test
- google test
- Google Test
- Google Test and Google Mock
- google api test
- Google-video test
- google map test
- another google api test
- google test 入门介绍
- google test的使用
- Google Test 学习笔记
- Google Related Links test
- google test初步分析
- /usr/bin/ld: cannot find -lmysqlclient 解决方法
- Android面试汇总(百度+360+Tencent+淘宝+Qualcomm+HTC)
- JavaScript DOM
- 浅谈浏览器插件检测 和自定义协议的支持
- js实现全选、反选功能(批量删除)
- Google Test
- mysql出错的时候怎么退出来
- java的System类
- “悦”在2014
- 6章第13题
- 如何解决word2007弹出“发送命令失败”
- android studio 快捷键
- ROOT ASUS FONEPAD7 FE170CG/FE7010CG (K012) KITKAT 4.4.2
- 使用new和newInstance()创建类的区别