Qt高級——QTestLib單元測試框架-創(chuàng)新互聯(lián)

Qt高級——QTestLib單元測試框架

一、QTestLib簡介

1、QTestLib簡介

QTestLib是Qt提供的一種針對基于Qt編寫的程序或庫的單元測試框架。QTestLib提供了單元測試框架的基本功能,并提供了針對GUI測試的擴展功能。

為敘州等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計制作服務(wù),及敘州網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為成都網(wǎng)站制作、做網(wǎng)站、外貿(mào)營銷網(wǎng)站建設(shè)、敘州網(wǎng)站設(shè)計,以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!

2、QTestLib特性

QTestLib是為了簡化QT程序或庫的單元測試工作而設(shè)計的。
QTestLib特性如下:
A、輕量級:QTestlib只包含6000行代碼和60個導(dǎo)出符號
B、自包含:對于非GUI測試,QTestlib只需要Qt核心庫的幾個符號。
C、快速測試:QTestlib不需要特殊的測試執(zhí)行程序,不需要為測試而進(jìn)行特殊的注冊。
D、數(shù)據(jù)驅(qū)動測試:一個測試程序可以在不同的測試數(shù)據(jù)集上執(zhí)行多次。
E、基本的GUI測試:QTestlib提供了模擬鼠標(biāo)和鍵盤事件的功能。
F、基準(zhǔn)測試:QTestLIB支持基準(zhǔn)測試并提供多種測量后端。
G、IDE友好:QTestlib的輸出信息可以被Visual?Studio和KDevelop解析。
H、線程安全:錯誤報告是線程安全的、原子性的。
J、類型安全:對模板進(jìn)行了擴展使用,防止由隱式類型轉(zhuǎn)換引起的錯誤。
K、易擴展:用戶自定義類型可以容易地加入到測試數(shù)據(jù)和測試輸出中。

3、QTestLib API

所有公有的方法都在QTest命名空間中。另外,QSignalSpy類為Qt的信號和槽提供了簡單的內(nèi)省機制。

4、AutoTest插件

默認(rèn)測試結(jié)果以純文本形式顯示在控制臺(應(yīng)用程序輸出標(biāo)簽),不夠直觀,可使用AutoTest插件實現(xiàn)可視化效果。
通過Help->About Plugins->Utilities,選中AutoTest,重啟Qt Creator,然后在下方會多出TestResults的標(biāo)簽,可直接在此標(biāo)簽點擊上方的運行按鈕運行所有測試,同時在“Tools-Tests-Run All Tests”也可運行所有測試。
此插件可以在運行單元測試后以紅、綠色表示明確標(biāo)記處運行結(jié)果,并且以Case為單位顯示,可以展開看到具體每一個測試用例的結(jié)果。
Qt高級——QTestLib單元測試框架

二、QTestLib單元測試原理

1、QTestLib單元測試原理簡介

原理:輸入測試數(shù)據(jù)表和結(jié)果數(shù)據(jù)表,與實際值比較。

2、測試類

測試類需要從QObject類繼承,類中需要加入一個或者多個私有槽。每一個私有槽都是一個測試函數(shù),但有4種私有槽不能作為測試函數(shù),它們由測試框架執(zhí)行,可為整個測試程序或當(dāng)前測試函數(shù)進(jìn)行初始化和清除操作。
initTestCase():在第一個測試函數(shù)執(zhí)行前調(diào)用。
cleanupTestCase():在最后一個測試函數(shù)執(zhí)行后調(diào)用。
init():在每一個測試函數(shù)執(zhí)行前調(diào)用。
cleanup():在每一個測試函數(shù)執(zhí)行后調(diào)用。
如果initTestCase()函數(shù)執(zhí)行失敗,任何測試函數(shù)都不會執(zhí)行。如果init()函數(shù)執(zhí)行失敗,緊隨其后的測試函數(shù)不會被執(zhí)行,測試會繼續(xù)處理下一個測試函數(shù)。
QTest::qExec(QObject* testClassObject)函數(shù)用于執(zhí)行測試對象中所有的測試函數(shù)。

3、測試函數(shù)

對于一個要測試的目標(biāo)函數(shù),需要使用兩個函數(shù)進(jìn)行測試:testFunctionName()和testFunctionName_data()。
testFunctionName_data:數(shù)據(jù)提供,在函數(shù)體中寫入測試數(shù)據(jù)。
testFunctionName:測試的實體,讀取testFunctionName_data函數(shù)中的數(shù)據(jù)表,并逐行進(jìn)行測試。如果測試結(jié)果與數(shù)據(jù)表中的結(jié)果不同,則認(rèn)為測試失敗。

4、測試數(shù)據(jù)構(gòu)建

數(shù)據(jù)由QTest::addColumn < T > (name)和QTest::newRow(name) << input << result來構(gòu)建一個數(shù)據(jù)表,其中的列可以被獲取,然后將表中對應(yīng)的數(shù)據(jù)按行測試,并與表中的結(jié)果列進(jìn)行對比。

5、GUI測試

對于GUI交互操作的測試,則將數(shù)據(jù)設(shè)置為事件列表,供模擬測試。QTestlib提供了模擬鼠標(biāo)和鍵盤事件的功能。

6、通信

QTest提供一系列宏來進(jìn)行數(shù)據(jù)的通信。

QBENCHMARK
QBENCHMARK_ONCE
QCOMPARE(actual, expected)
QEXPECT_FAIL(dataIndex, comment, mode)
QFAIL(message)
QFETCH(type, name)
QFINDTESTDATA(filename)
QSKIP(description)
QTEST(actual, testElement)
QTEST_APPLESS_MAIN(TestClass)
QTEST_GUILESS_MAIN(TestClass)
QTEST_MAIN(TestClass)
QTRY_COMPARE(actual, expected)
QTRY_COMPARE_WITH_TIMEOUT(actual, expected, timeout)
QTRY_VERIFY2(condition, message)
QTRY_VERIFY(condition)
QTRY_VERIFY2_WITH_TIMEOUT(condition, message, timeout)
QTRY_VERIFY_WITH_TIMEOUT(condition, timeout)
QVERIFY2(condition, message)
QVERIFY(condition)
QVERIFY_EXCEPTION_THROWN(expression, exceptiontype)
QWARN(message)

7、程序啟動入口

QTest提供了QTEST_MAIN()作為測試的啟動宏,構(gòu)建一個main函數(shù),在main函數(shù)內(nèi)調(diào)用QTest::qExec(QObject testClassObject),也可以直接調(diào)用QTest::qExec(QObject testClassObject)來啟動測試。

三、簡單測試程序

1、編寫測試程序

假設(shè)要測試QString類的行為。首先,需要一個用于包含測試函數(shù)的必須從QObject繼承的類:

#include <QtTest/QtTest>

 class TestQString: public QObject
 {
     Q_OBJECT
 private slots:
     void toUpper();
 };

注意:需要包含QTest頭文件,并且測試函數(shù)必須聲明為私有槽,便于測試框架找到并執(zhí)行它們。

void TestQString::toUpper()
 {
     QString str = "Hello";
     QVERIFY(str.toUpper() == "HELLO");
 }

QVERIFY()宏將計算傳入的表達(dá)式的值。如果為真,則測試函數(shù)繼續(xù)進(jìn)行;否則會向測試日志中增加一條描述錯誤的信息,并且該測試函數(shù)會停止執(zhí)行。
但是如果需要向測試日志中增加更詳細(xì)的輸出信息,應(yīng)該使用QCOMPARE()宏:

void TestQString::toUpper()
 {
     QString str = "Hello";
     QCOMPARE(str.toUpper(), QString("HELLO"));
 }

2、執(zhí)行測試程序

寫完測試程序后就需要執(zhí)行測試程序。假設(shè)將測試程序命名為testqstring.cpp并保存在一個空目錄中,可以使用qmake生成一個工程文件和一個Makefile文件。

myTestDirectory$ qmake -project "QT += qtestlib"
    myTestDirectory$ qmake
    myTestDirectory$ make

Qt高級——QTestLib單元測試框架

3、QTestlib命令行參數(shù)

執(zhí)行自動測試的語法形式:
testname?[options]?[testfunctions[:testdata]]...
testname:測試項目的可執(zhí)行文件
testfunctions:包含要執(zhí)行的測試函數(shù)名,如果不指定testfunctions,所有的測試函數(shù)都會執(zhí)行。如果測試函數(shù)名之后加上了測試數(shù)據(jù)行的名字,則測試函數(shù)執(zhí)行時只會使用該行測試數(shù)據(jù)。
列如:
/myTestDirectory#?StringTest toUpper
使用所有的測試數(shù)據(jù)執(zhí)行toUpper測試函數(shù)。
/myTestDirectory$?StringTest??toUpper??toInt:zero
使用所有的測試數(shù)據(jù)執(zhí)行toUpper測試函數(shù),使用行名為zero的測試數(shù)據(jù)執(zhí)行toInt測試函數(shù)(如果對應(yīng)的測試數(shù)據(jù)不存在,相關(guān)的測試執(zhí)行時就會失?。?。
/myTestDirectory$?WidgetTest??-vs??-eventdelay??500
執(zhí)行WidgetTest測試程序,輸出每一個信號發(fā)射信息,在每次模擬鼠標(biāo)/鍵盤事件之后等待500毫秒。
選項
下列命令行參數(shù)可以被接受:
-help
輸出命令行參數(shù)的幫助信息。
-functions?
輸出測試中的所有測試函數(shù)。
-o?filename?
將輸出信息寫入到執(zhí)行文件中,而不是打印到標(biāo)準(zhǔn)輸出上。
-silent?
沉默地輸出,只顯示警告、錯誤和最少的狀態(tài)信息。
-v1?
詳細(xì)輸出;輸出每次進(jìn)入或離開測試函數(shù)的信息。
-v2?
詳細(xì)輸出;也輸出每個QCOMPARE()和QVERIFY()信息。
-vs?
輸出發(fā)出的所有信號。
-xml?
將輸出格式化成XML格式,而不是普通文本
-lightxml?
輸出成XML標(biāo)簽流。
-eventdelay?ms?
如果鍵盤或鼠標(biāo)模擬(QTest::keyClick(),QTest::mouseClick()等)不指定延遲時間,則使用該參數(shù)(以毫秒為單位)作為延遲時間。
-keydelay?ms
與-eventdelay的作用一樣,但只影響鍵盤模擬的延遲時間,不影響鼠標(biāo)模擬的延遲時間。
-mousedelay?ms?
與-eventdelay的作用一樣,但只影響鼠標(biāo)模擬的延遲時間,不影響鍵盤模擬的延遲時間。
-keyevent-verbose?
詳細(xì)輸出鍵盤模擬信息。
-maxwarnings?numberBR?
設(shè)置警告信息的大數(shù)量,0表示不限制,默認(rèn)值為2000。

四、數(shù)據(jù)驅(qū)動測試程序

1、數(shù)據(jù)驅(qū)動測試簡介

目前為止,采用硬編碼的方式將測試數(shù)據(jù)寫到測試函數(shù)中。如果增加更多的測試數(shù)據(jù),那么測試函數(shù)會變成:

QCOMPARE(QString("hello").toUpper(), QString("HELLO"));
    QCOMPARE(QString("Hello").toUpper(), QString("HELLO"));
    QCOMPARE(QString("HellO").toUpper(), QString("HELLO"));
    QCOMPARE(QString("HELLO").toUpper(), QString("HELLO"));

為了不使測試函數(shù)被重復(fù)的代碼弄得凌亂不堪, QTestLib支持向測試函數(shù)增加測試數(shù)據(jù),僅需要向測試類增加另一個私有槽:

class TestQString: public QObject
 {
     Q_OBJECT
 private slots:
     void toUpper_data();
     void toUpper();
 };

2、編寫測試數(shù)據(jù)函數(shù)

為測試函數(shù)提供數(shù)據(jù)的函數(shù)必須與測試函數(shù)同名,并加上_data后綴。為測試函數(shù)提供數(shù)據(jù)的函數(shù)類似這樣:

void TestQString::toUpper_data()
 {
     QTest::addColumn<QString>("string");
     QTest::addColumn<QString>("result");

     QTest::newRow("all lower") << "hello" << "HELLO";
     QTest::newRow("mixed")     << "Hello" << "HELLO";
     QTest::newRow("all upper") << "HELLO" << "HELLO";
 }

首先,使用QTest::addColumn()函數(shù)定義測試數(shù)據(jù)表的兩列元素:測試字符串和在該測試字符串上調(diào)用QString::toUpper()函數(shù)期望得到的結(jié)果。
然后使用 QTest::newRow()函數(shù)向測試數(shù)據(jù)表中增加一些數(shù)據(jù)。每組數(shù)據(jù)都會成為測試數(shù)據(jù)表中的一個單獨的行。
QTest::newRow()函數(shù)接收一個參數(shù):將要關(guān)聯(lián)到該行測試數(shù)據(jù)的名字。如果測試函數(shù)執(zhí)行失敗,名字會被測試日志使用,以引用導(dǎo)致測試失敗的數(shù)據(jù)。然后將測試數(shù)據(jù)加入到新行:首先是一個任意的字符串,然后是在該行字符串上調(diào)用 QString::toUpper()函數(shù)期望得到的結(jié)果字符串。
可以將測試數(shù)據(jù)看作是一張二維表格。在這個例子里,它包含兩列三行,列名為string 和result。另外,每行都會對應(yīng)一個序號和名稱:
index name string result
0 all lower "hello" HELLO
1 mixed "Hello" HELLO
2 all upper "HELLO" HELLO

3、編寫測試函數(shù)

測試函數(shù)需要被重寫:

void TestQString::toUpper()
 {
     QFETCH(QString, string);
     QFETCH(QString, result);

     QCOMPARE(string.toUpper(), result);
 }

TestQString::toUpper()函數(shù)會執(zhí)行兩次,對toUpper_data()函數(shù)向測試數(shù)據(jù)表中加入的每一行都會調(diào)用一次。
首先,調(diào)用QFETCH()宏從測試數(shù)據(jù)表中取出兩個元素。QFETCH()接收兩個參數(shù): 元素的數(shù)據(jù)類型和元素的名稱。然后用QCOMPARE()宏執(zhí)行測試操作。
使用這種方法可以不修改測試函數(shù)就向該函數(shù)加入新的數(shù)據(jù)。
像以前一樣,為使測試程序能夠單獨執(zhí)行,需要加入下列代碼:
QTEST_MAIN(TestGui)
QTEST_MAIN()宏將擴展成一個簡單的main()函數(shù),該main()函數(shù)會執(zhí)行所有的測試函數(shù)。

五、GUI測試

QTestlib單元測試提供GUI操作函數(shù),可對控件發(fā)送消息后檢測執(zhí)行結(jié)果,比如QTest::keyClick(),QTest::mouseClick()等等

1、模擬GUI事件

QTestlib具有測試GUI的一些特性。QTestLib發(fā)送內(nèi)部Qt事件,而不是模擬本地窗口系統(tǒng)事件,因此運行測試程序不會對機器產(chǎn)生任何副作用。

#include <QtGui>
#include <QtTest/QtTest>

 class TestGui: public QObject
 {
     Q_OBJECT
 private slots:
     void testGui();
 };

唯一的區(qū)別是除了要加入QTest命名空間之外,需要包含QtGui類的定義。

void TestGui::testGui()
 {
     QLineEdit lineEdit;
     QTest::keyClicks(&lineEdit, "hello world");
     QCOMPARE(lineEdit.text(), QString("hello world"));
 }

在測試函數(shù)實現(xiàn)中,創(chuàng)建一個QLineEdit,使用QTest::keyClicks()函數(shù)模擬在行編輯框中輸入“hello world”字符串。
注意: 為了正確測試快捷鍵,控件必須顯示出來。
QTest::keyClicks()在控件上模擬一連串的鍵盤敲擊操作。另外,每次鍵盤敲擊后,可以指定延遲時間(以毫秒為單位)。同樣,也可以用 QTest::keyClick()、QTest::keyPress()、QTest::keyRelease()、QTest::mouseClick()、QTest::mouseDClick()、QTest::mouseMove()、QTest::mousePress() 和QTest::mouseRelease()函數(shù)來模擬相應(yīng)的GUI事件。
最后,使用QCOMPARE()宏來檢驗行編輯框的文本是否與預(yù)期的一致。
像前面一樣,為使測試程序能夠單獨執(zhí)行,需要加入下列代碼:
QTEST_MAIN(TestGui)
QTEST_MAIN()宏將擴展成一個簡單的main()函數(shù),該main()函數(shù)會執(zhí)行所有的測試函數(shù)。

2、重復(fù)GUI事件

在本節(jié)中,將展示如何模擬GUI事件,以及如何存儲一系列GUI事件以及如何在組件上重復(fù)這些GUI事件。
將一系列GUI事件保存起來并重復(fù)觸發(fā)的方法與數(shù)據(jù)驅(qū)動測試程序的方法很類似。所要做的只是向測試類增加一個提供測試數(shù)據(jù)的函數(shù):

class TestGui: public QObject
 {
     Q_OBJECT
 private slots:
     void testGui_data();
     void testGui();
 };

像前面一樣,為測試函數(shù)提供數(shù)據(jù)的函數(shù)必須與該測試函數(shù)同名,并加上_data后綴。

void TestGui::testGui_data()
 {
     QTest::addColumn<QTestEventList>("events");
     QTest::addColumn<QString>("expected");

     QTestEventList list1;
     list1.addKeyClick('a');
     QTest::newRow("char") << list1 << "a";

     QTestEventList list2;
     list2.addKeyClick('a');
     list2.addKeyClick(Qt::Key_Backspace);
     QTest::newRow("there and back again") << list2 << "";
 }

首先,用QTest::addColumn()函數(shù)定義測試數(shù)據(jù)表的元素:一個GUI事件列表,以及在控件上應(yīng)用該事件列表預(yù)期得到的結(jié)果。注意第一個元素的類型是QTestEventList。
QTestEventList可以保存將來要使用的GUI事件,并可以在任意控件上重復(fù)觸發(fā)這些事件。
在目前的提供測試數(shù)據(jù)的函數(shù)中,創(chuàng)建了兩個QTestEventLists。第一個鏈表包括了一個敲擊“a“鍵事件,調(diào)用QTestEventList::addKeyClick()函數(shù)向鏈表中加入該事件。然后用QTest::newRow()函數(shù)給該行數(shù)據(jù)指定一個名字,并把事件隊列和期望結(jié)果輸入到測試數(shù)據(jù)表中。
第二個鏈表包括兩次鍵盤敲擊:一個“a“,然后是一個“backspace“。同樣用 QTestEventList::addKeyClick()函數(shù)將事件加入隊列,用QTest::newRow()將事件隊列和期望的結(jié)果加入測試數(shù)據(jù)表中,并為該行指定一個名字。

void TestGui::testGui()
 {
     QFETCH(QTestEventList, events);
     QFETCH(QString, expected);

     QLineEdit lineEdit;

     events.simulate(&lineEdit);

     QCOMPARE(lineEdit.text(), expected);
 }

TestGui::testGui()函數(shù)會執(zhí)行兩次,對在TestGui::testGui_data()函數(shù)中創(chuàng)建的每一行測試數(shù)據(jù)都執(zhí)行一次。
首先,用QFETCH()宏從測試數(shù)據(jù)集中取出兩個元素。QFETCH()宏接收兩個參數(shù):元素的數(shù)據(jù)類型和元素的名字。然后創(chuàng)建了一個QLineEdit,調(diào)用 QTestEventList::simulate()函數(shù)在控件上觸發(fā)事件隊列。
最后,用QCOMPARE()宏檢測行編輯框的內(nèi)容是否與期望的一致。
像以前一樣,為使測試程序能夠單獨執(zhí)行,需要加入下列代碼:
QTEST_MAIN(TestGui)
QTEST_MAIN()宏將擴展成一個簡單的main()函數(shù),該main()函數(shù)會執(zhí)行所有的測試函數(shù)。

六、Benchmark測試

為了編寫一個基準(zhǔn)測試程序,需要使用QBENCHMARK宏來擴展測試函數(shù)。一個基準(zhǔn)測試函數(shù)通常由初始化代碼和一個QBENCHMARK宏組成,QBENCHMARK宏包含了需要被測試的代碼。

1、編寫一個基準(zhǔn)測試函數(shù)

測試函數(shù)會對QString::localeAwareCompare()函數(shù)進(jìn)行基準(zhǔn)測試。

void TestBenchmark::simple()
 {
     QString str1 = QLatin1String("This is a test string");
     QString str2 = QLatin1String("This is a test string");

     QCOMPARE(str1.localeAwareCompare(str2), 0);

     QBENCHMARK {
         str1.localeAwareCompare(str2);
     }
 }

初始化部分將在函數(shù)的開頭被完成,但時鐘并不在這點運行。內(nèi)嵌在QBENCHMARK宏中的代碼將被估量,并且為了得出精確的測量將會被重復(fù)數(shù)次。

2、多數(shù)據(jù)輸入的基準(zhǔn)測試

當(dāng)創(chuàng)建對多個數(shù)據(jù)輸入進(jìn)行比較的基準(zhǔn)測試時,數(shù)據(jù)函數(shù)是有用的。

void TestBenchmark::multiple_data()
 {
     QTest::addColumn<bool>("useLocaleCompare");
     QTest::newRow("locale aware compare") << true;
     QTest::newRow("standard compare") << false;
 }

測試函數(shù)使用輸入數(shù)據(jù)決定什么被基準(zhǔn)測試。

void TestBenchmark::multiple()
 {
     QFETCH(bool, useLocaleCompare);
     QString str1 = QLatin1String("This is a test string");
     QString str2 = QLatin1String("This is a test string");

     int result;
     if (useLocaleCompare) 
     {
         QBENCHMARK {
             result = str1.localeAwareCompare(str2);
         }
     } 
     else 
     {
         QBENCHMARK {
             result = (str1 == str2);
         }
     }
 }

“if(useLocaleCompare)”開關(guān)放在QBENCHMARK宏外部避免測量開銷。每個基準(zhǔn)測試函數(shù)可以有一個在用的QBENCHMARK宏。

七、注意事項

A、單元測試類中建議不要出現(xiàn)私有成員,尤其是指針,同時不建議在測試函數(shù)中建立被測類的指針,而是直接建立被測類的對象,在測試結(jié)束后容易遺忘指針。若需要指針,在initTestCase函數(shù)中new,在cleanupTestCase函數(shù)中delete。
B、若某個測試函數(shù)中出現(xiàn)了new,一定記著delete,且務(wù)必讓delete在第一個斷言前出現(xiàn),因為斷言失敗函數(shù)就回立刻結(jié)束,并把當(dāng)前函數(shù)標(biāo)記為測試失敗。若delete在第一個斷言之后,而第一個斷言失敗則不會執(zhí)行之后的delete。
C、若測試類必須有私有成員,必須注意一個測試類中的所有函數(shù)公用私有成員,不會在每個測試之前刷新狀態(tài)。
D、若被測類為單例,欲對其內(nèi)所有函數(shù)做單元測試,會出現(xiàn)測試第一個函數(shù)可以保證測試環(huán)境為初始狀態(tài),后續(xù)測試會因為單例的原因,導(dǎo)致測試時建立在之前操作后的環(huán)境下。欲解決此問題,需要刪除單例。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。

文章標(biāo)題:Qt高級——QTestLib單元測試框架-創(chuàng)新互聯(lián)
文章網(wǎng)址:http://muchs.cn/article18/pgddp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、商城網(wǎng)站、品牌網(wǎng)站制作、營銷型網(wǎng)站建設(shè)自適應(yīng)網(wǎng)站、品牌網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)