2013-07-28 7 views
10

मुझे सी ++ में लिखा गया एक प्रोग्राम मिला है, जिसमें कुछ सबफ़ोल्डर शामिल हैं जिसमें पुस्तकालय शामिल हैं। एक शीर्ष स्तर स्कॉन्स्क्रिप्ट है, जो उपफोल्डर/पुस्तकालयों में स्कॉन्स्क्रिप्ट फ़ाइलों को कॉल करता है।जीटीएस्ट अलग संकलन इकाइयों में परीक्षण नहीं ढूंढ रहा है

एक पुस्तकालय सीपीपी के अंदर, वहाँ एक GTest है:

TEST(X, just_a_passing_test) { 
EXPECT_EQ(true, true); 
} 

वहाँ() शीर्ष स्तर के कार्यक्रम स्रोत है, जो सिर्फ मुख्य GTests कॉल में मुख्य है, और एक अन्य GTest है:

int main(int argc, char** argv) { 
::testing::InitGoogleTest(&argc, argv); 
return RUN_ALL_TESTS(); 
} 

TEST(Dummy, should_pass){ 
EXPECT_EQ(true, true); 
} 

अब मुद्दा यह है कि जब मैं प्रोग्राम चलाता हूं, तो GTest केवल main.cpp स्रोत में परीक्षण चलाता है। पुस्तकालय में परीक्षण को अनदेखा करना। अब जब मैं किसी भी साइड-इफेक्ट प्रकार (जैसे 'SomeClass foo;') में एक ही लाइब्रेरी सीपीपी में एक असंबंधित कक्षा में संदर्भित करता हूं, तो यह विचित्र हो जाता है, परीक्षण जादूगर रूप से प्रकट होता है। मैंने कोड को ऑप्टिमाइज़ करने के लिए जीसीसी को मजबूर करने के लिए -O0 और अन्य चालों का उपयोग करने का प्रयास किया है जिसे कॉल नहीं किया गया है। मैंने क्लैंग भी कोशिश की है। मुझे संदेह है कि जीटीएस्ट संकलन के दौरान परीक्षण की खोज कैसे करता है, लेकिन मुझे इस मुद्दे पर कोई जानकारी नहीं मिल रही है। मेरा मानना ​​है कि यह स्थिर प्रारंभिकरण का उपयोग करता है, इसलिए शायद वहां कुछ अजीब आदेश चल रहा है। किसी भी मदद/जानकारी की बहुत सराहना की जाती है!

अद्यतन: विशेष रूप से विज़ुअल सी ++ को संदर्भित करने के बावजूद, इस समस्या की तरह लगता है कि FAQ में एक अनुभाग मिला। जिसमें संदर्भित नहीं होने पर लाइब्रेरी को त्यागने के लिए कंपाइलर को मजबूर करने के लिए एक चाल/हैक शामिल है। यह पुस्तकालयों में परीक्षण न डालने की सिफारिश करता है, लेकिन इससे मुझे आश्चर्य होता है कि आप पुस्तकालयों का परीक्षण कैसे करेंगे, बिना किसी के लिए निष्पादन योग्य, बिना उन्हें दर्द और सूजन आउटपुट के साथ। https://code.google.com/p/googletest/wiki/Primer#Important_note_for_Visual_C++_users

उत्तर

11

दृश्य स्थापित एक से बटोरता है कि पुस्तकालय जिसका gtest परीक्षण का मामला चला जाता लापता स्थिर आवेदन के निर्माण में जुड़ा हुआ है। इसके अलावा जीएनयू टूलचेन उपयोग में है।

समस्या व्यवहार का कारण सीधा है। परीक्षण प्रोग्राम में लाइब्रेरी में किसी भी चीज़ का कोई संदर्भ नहीं है जिसमें TEST(X, just_a_passing_test) शामिल है। तो प्रोग्रामर को लिंक करने के लिए उस लाइब्रेरी से ऑब्जेक्ट फ़ाइल को जोड़ने के लिए लिंकर की आवश्यकता नहीं है। तो यह नहीं करता है। तो gtest रनटाइम निष्पादन योग्य में उस परीक्षण को नहीं ढूंढता है, क्योंकि यह वहां नहीं है।

यह समझने में सहायता करता है कि जीएनयू प्रारूप में एक स्थिर लाइब्रेरी एक ऑब्जेक्ट फाइलों का संग्रह है, जो घर-रखरखाव हेडर ब्लॉक और वैश्विक प्रतीक तालिका से सजा है।

ओपी पता चला कि कार्यक्रम में समस्या पुस्तकालय में किसी भी सार्वजनिक प्रतीक के लिए एक तदर्थ संदर्भ कोडिंग से, वह "जादुई" अपने परीक्षण का मामला कार्यक्रम में दबाव डाल सकता है।

कोई जादू नहीं। उस सार्वजनिक प्रतीक के संदर्भ को पूरा करने के लिए, लिंकर अब लाइब्रेरी से ऑब्जेक्ट फ़ाइल को लिंक करने के लिए बाध्य है - जिसमें प्रतीक की परिभाषा शामिल है। और ओपी प्रदान करता है कि .cpp से पुस्तकालय बनाया गया है। तो लाइब्रेरी में केवल एक ऑब्जेक्ट फ़ाइल है, और यह में टेस्ट केस की परिभाषा भी है। उस ऑब्जेक्ट फ़ाइल के साथ लिंकेज में, परीक्षण केस प्रोग्राम में है।

ओपी एक ही अंत प्राप्त करने के लिए एक और सम्मानजनक तरीके की खोज में जीसीसी से क्लैंग, पर स्विचिंग, कंपाइलर विकल्पों के साथ व्यर्थ में twiddled। कंपाइलर अप्रासंगिक है। जीसीसी या क्लैंग, यह सिस्टम लिंकर, ld द्वारा इसकी लिंकिंग प्राप्त करता है (जब तक इसे बदलने के लिए असामान्य उपाय नहीं किए जाते हैं)।

वहाँ ld उस वस्तु फ़ाइल में कोई प्रतीकों को भी जब कार्यक्रम संदर्भित करता है एक स्थिर पुस्तकालय से एक वस्तु फ़ाइल से जोड़ने के लिए प्राप्त करने के लिए एक और अधिक सम्मानजनक रास्ता नहीं है?

वहाँ है। कहो समस्या कार्यक्रम app है और समस्या को स्थिर पुस्तकालय libcool.a

फिर हमेशा की तरह जीसीसी कमांडलाइन कि app लिंक प्रासंगिक अंक में इस जैसा दिखता है, यह है: ld को

g++ -o app -L/path/to/the/libcool/archive -lcool 

यह प्रतिनिधियों कमांडलाइन साथ, अतिरिक्त लिंकर विकल्प और लाइब्रेरीज़ g++ सिस्टम के लिए डिफ़ॉल्ट होने लगता है जहां यह स्वयं पाता है।

जब लिंकर -lcool पर विचार करने के लिए आता है, तो यह पता लगाएगा कि यह /path/to/the/libcool/archive/libcool.a संग्रह के लिए अनुरोध है। फिर यह पता लगाएगा कि इस बिंदु पर अभी भी हाथ में किसी भी अनसुलझे प्रतीक संदर्भ प्राप्त हुए हैं जिनकी परिभाषा libcool.a में ऑब्जेक्ट फ़ाइलों में संकलित की गई है। यदि कोई है, तो यह उन ऑब्जेक्ट फ़ाइलों को app से लिंक करेगा। यदि नहीं, तो यह libcool.a से कुछ भी लिंक नहीं करता है और आगे बढ़ता है।

लेकिन हम जानते हैं कि libcool.a में प्रतीक परिभाषाएं हैं कि हम लिंक चाहते हैं, भले ही app उन्हें संदर्भित न करें। उस स्थिति में, हम ऑब्जेक्ट फ़ाइलों को libcool.a से लिंक करने के लिए लिंकर बता सकते हैं भले ही वे संदर्भित न हों। दरअसल, हम g++ बताते हैं कि ऐसा करने के लिए, तो जैसे लिंकर बताने के लिए कर सकते हैं:

g++ -o app -L/path/to/the/libcool/archive -Wl,--whole-archive -lcool -Wl,-no-whole-archive 

उन -Wl,... विकल्पों g++ बता ...ld करने के लिए विकल्पों पारित करने के लिए। --whole-archive विकल्प ld को बाद के अभिलेखागार से सभी ऑब्जेक्ट फ़ाइलों को जोड़ने के लिए बताता है, चाहे वे संदर्भित हों या नहीं, आगे की सूचना तक। -no-whole-archive ऐसा करने से रोकने के लिए ld बताता है और सामान्य रूप से व्यवसाय को फिर से शुरू करता है।

ऐसा लगता है कि -Wl,-no-whole-archive अनावश्यक है, क्योंकि यह g++ कमांडलाइन पर आखिरी बात है। लेकिन ऐसा नहीं है। याद रखें कि g++ld पर जाने से पहले, दृश्यों के पीछे, कमांडलाइन पर सिस्टम डिफ़ॉल्ट लाइब्रेरी जोड़ता है। आप निश्चित रूप से --whole-archive नहीं चाहते हैं जब वे डिफ़ॉल्ट पुस्तकालय जुड़े हों। (लिंक कई परिभाषा त्रुटियों के साथ विफल हो जाएगा)।

समस्या मामले के लिए इस समाधान को लागू करें और TEST(X, just_a_passing_test) कार्यक्रम के लिए मजबूर कर वस्तु फ़ाइल है कि है कि परीक्षण को परिभाषित करता है में कुछ नहीं-op संदर्भ बनाने के लिए की हैक बिना, निष्पादित किया जाएगा।

सामान्य स्थिति में इस समाधान के लिए एक स्पष्ट नकारात्मक पक्ष नहीं है। यदि ऐसा होता है कि से पुस्तकालय जो हम कुछ unreferenced वस्तु फ़ाइल के संबंध बाध्य करना चाहते हैं अन्य unreferenced वस्तु फ़ाइलों है कि हम वास्तव में की जरूरत नहीं है के गुच्छा होता है। --whole-archive उन सभी को भी उनसे लिंक करता है, और वे प्रोग्राम में बस फंस गए हैं।

--whole-archive समाधान अधिक सम्मानजनक हो सकता है कि कोई-op संदर्भ हैक, लेकिन यह सम्मानजनक नहीं है। यह भी सम्मानजनक नहीं है।

यहाँ वास्तविक समाधान सिर्फ उचित बात करना है। आप लिंकर अपने कार्यक्रम में कुछ की परिभाषा से जोड़ने के लिए चाहते हैं, तो लिंकर से एक रहस्य नहीं है कि नहीं रखते। कम से कम प्रत्येक संकलन इकाई में बात जहां इसकी परिभाषा में इस्तेमाल किया जा करने की उम्मीद घोषणा करते हैं।

class X_just_a_passing_test_Test : public ::testing::Test { 
public: 
    X_just_a_passing_test_Test() {} 
private: 
    virtual void TestBody(); 
    static ::testing::TestInfo* const test_info_ __attribute__ ((unused)); 
    X_just_a_passing_test_Test(X_just_a_passing_test_Test const &); 
    void operator=(X_just_a_passing_test_Test const &); 
}; 

(प्लस test_info_ के लिए एक स्थिर प्रारंभकर्ता और:

gtest परीक्षण मामलों के साथ उचित बात करने से समझ है कि एक gtest मैक्रो TEST(X, just_a_passing_test) की तरह इस मामले में एक वर्ग परिभाषा, के लिए विस्तारित शामिल TestBody() के लिए एक परिभाषा)।

TEST_F, TEST_P वेरिएंट के लिए इसी तरह

। नतीजतन, आप अपने कोड में इन मैक्रोज़ को केवल उसी बाधाओं और अपेक्षाओं के साथ तैनात कर सकते हैं जो कक्षा परिभाषाओं पर लागू होते हैं।

इस रोशनी में, यदि आप इसके लिए एक पुस्तकालय libcoolcool.h में परिभाषित किया गया, cool.cpp में लागू किया है और चाहते हैं gtest इकाई परीक्षणों में परीक्षण कार्यक्रम tests कि tests.cpp में कार्यान्वित किया जाता, उचित बात है द्वारा निष्पादित की जाने: -

  • एक हेडर फाइल लिखें, cool_test.h
  • उस में #include "cool.h"
  • #include <gtest/gtest.h> इसमें।
  • फिर, यह
  • tests.cpp में #include "cool_test.h" में अपने libcool परीक्षण मामलों को परिभाषित
  • संकलित करें और libcool और libgtest

साथ लिंक tests.cpp और यह स्पष्ट है कि आप क्यों नहीं होता क्या ओपी किया है। आप कक्षाएं कि tests.cpp द्वारा की जरूरत है, और परिभाषित नहीं होता cool.cpp की जरूरत नहीं, cool.cpp और tests.cpp में नहीं हैं।

ओपी पुस्तकालय में परीक्षण मामलों को परिभाषित करने के खिलाफ सलाह के खिलाफ था है क्योंकि:

और कैसे आप पुस्तकालयों का परीक्षण होता है, हर एक, जल्दी से बनाने के लिए एक निष्पादन योग्य होने के लिए उन्हें एक दर्द चलने के बिना ।

अंगूठे का एक नियम के रूप में मैं एक gtest निष्पादन पुस्तकालय प्रति बनाए रखने के अभ्यास की सिफारिश करेंगे यूनिट परीक्षण किया जाना: उन्हें जल्दी से चल रहा है आम स्वचालन उपकरण इस तरह के एक make साथ दर्द रहित है, और यह अब तक प्राप्त करने के लिए बेहतर है एक पास/से सिर्फ पुस्तकालयों का एक गुच्छा के लिए एक फैसले असफल पुस्तकालय प्रति फैसले। लेकिन आप अभी भी आपत्ति करने के लिए कुछ भी नहीं है कि ऐसा करने के लिए नहीं करना चाहते हैं:

// tests.cpp 
#include "cool_test.h" 
#include "cooler_test.h" 
#include "coolest_test.h" 

int main(int argc, char** argv) { 
    ::testing::InitGoogleTest(&argc, argv); 
    return RUN_ALL_TESTS(); 
} 

संकलित करें और लिंक के साथ libcool, libcooler, libcoolest और libgtest

+0

विस्तृत जानकारी के लिए धन्यवाद। यह मेरा शक की पुष्टि में मदद करता है। अंत में मैं इकाई परीक्षण चलाने के लिए प्रत्येक पुस्तकालय के लिए निष्पादन योग्य लक्ष्य का इस्तेमाल किया। – NitrousUK

+0

+50 अच्छे उदाहरण के साथ ग्रेट जवाब! –

+0

मेरी मामले में मच-ओ प्रकार में पुनर्निधारणीय वस्तु फाइल करने के लिए स्टेटिक पुस्तकालय से Xcode परिवर्तन –

संबंधित मुद्दे