รับสตริงสุดท้ายที่พิมพ์ด้วย C/C++

ฉันกำลังพยายามสร้างผู้ทดสอบโดยใช้ googletest ปัญหาคือฟังก์ชันที่ฉันกำลังทดสอบคืนค่าเป็นโมฆะและพิมพ์ผลลัพธ์แทน ฉันต้องการพิมพ์สตริงสุดท้ายลงในคอนโซลเพื่อทดสอบเอาต์พุต สตริงอาจรวมถึง \n

ดังนั้นฉันจึงมีฟังก์ชันของตัวเอง:

void f_sequence(char sequenceStr[])
{
   //logic...
    if(condotion1)
        printf("somthing1");
    else if(condotion2)
        printf("somthing2")
(...)
}

แล้วผู้ทดสอบ:

TEST(TesterGroup, TesterName)
{
    f_sequence("input");
    EXPECT_EQ("somthing1", /*how do i get the output?*/);
}

เป็นไปได้ไหม?

ฟังก์ชั่นที่ฉันทดสอบอยู่ใน c ในขณะที่ฟังก์ชั่นการทดสอบเอง (ผู้ทดสอบ) อยู่ใน c++ เอาต์พุตถูกพิมพ์โดยใช้ printf ฉันไม่สามารถเปลี่ยนฟังก์ชันเองได้ ฉันใช้ CLion เวอร์ชันล่าสุด


person avivgood2    schedule 19.02.2020    source แหล่งที่มา
comment
คุณสามารถเปลี่ยนฟังก์ชั่นให้ยอมรับพารามิเตอร์ std::ostream& แทนที่จะพิมพ์เป็น stdout/stderr ได้หรือไม่   -  person jtbandes    schedule 19.02.2020
comment
คหรือค++? เป็นภาษาที่แตกต่างกันและวิธีแก้ปัญหาจะแตกต่างกันอย่างมาก (และไม่มีภาษาที่เรียกว่า C/C++ นั่นเป็นคำเรียกชื่อผิดที่น่าเสียดายที่ใช้กันทั่วไป)   -  person 463035818_is_not_a_number    schedule 19.02.2020
comment
@ idclev463035818 ฟังก์ชั่นที่ฉันทดสอบอยู่ใน c ในขณะที่ฟังก์ชั่นการทดสอบเอง (ผู้ทดสอบ) อยู่ใน c ++   -  person avivgood2    schedule 19.02.2020
comment
@ avivgood2 - โพสต์โค้ด (ขั้นต่ำ) บางส่วนที่แสดงให้เห็นถึงปัญหา   -  person Happy Green Kid Naps    schedule 19.02.2020
comment
@jtbandes มันถูกพิมพ์โดยใช้ printf และฉันไม่สามารถเปลี่ยนแปลงได้ เพียงแค่เขียนผู้ทดสอบ   -  person avivgood2    schedule 19.02.2020
comment
ไม่ใช่หัวข้อของฉันที่จะช่วยเหลือ แต่คุณควรให้ตัวอย่างที่ทำซ้ำได้น้อยที่สุด รายละเอียดมีความสำคัญ   -  person 463035818_is_not_a_number    schedule 19.02.2020
comment
คุณสามารถเปลี่ยนเส้นทาง stdout ไปยังไฟล์ จากนั้นอ่านจากไฟล์นั้นเมื่อฟังก์ชัน C ของคุณกลับมา   -  person Ruslan    schedule 19.02.2020
comment
ฟังก์ชันนี้ไม่สามารถทดสอบได้เหมือนเดิม ฉันอยากจะแนะนำคำแนะนำ @jtbandes   -  person andre    schedule 19.02.2020
comment
มีซอร์สโค้ด ที่นี่ (สำหรับแอปพลิเคชัน Windows เท่านั้น) ที่สร้าง API สำหรับการส่ง cmd และอ่าน stdout ลงในบัฟเฟอร์การปรับขนาดแบบไดนามิก ฉันได้ทดสอบมากถึง 2GBytes เมื่ออ่านการเรียกไดเรกทอรีแบบเรียกซ้ำไปยัง Windows ( นี่คือต้นฉบับ ก่อนทบทวน )   -  person ryyker    schedule 19.02.2020


คำตอบ (5)


เปลี่ยนเส้นทางเอาต์พุตมาตรฐานไปยังบัฟเฟอร์

ถ่ายทอดสดทาง Coliru

#include <stdio.h>
#include <unistd.h>

#define BUFFER_SIZE 1024
int stdoutSave;
char outputBuffer[BUFFER_SIZE];

void replaceStdout()
{
    fflush(stdout); //clean everything first
    stdoutSave = dup(STDOUT_FILENO); //save the stdout state
    freopen("NUL", "a", stdout); //redirect stdout to null pointer
    setvbuf(stdout, outputBuffer, _IOFBF, 1024); //set buffer to stdout
}

void restoreStdout()
{
    freopen("NUL", "a", stdout); //redirect stdout to null again
    dup2(stdoutSave, STDOUT_FILENO); //restore the previous state of stdout
    setvbuf(stdout, NULL, _IONBF, BUFFER_SIZE); //disable buffer to print to screen instantly
}

void printHelloWorld()
{
    printf("hello\n");
    printf("world");
}

int main()
{
    replaceStdout();
    printHelloWorld();
    restoreStdout();
    // Use outputBuffer to test EXPECT_EQ("somthing1", outputBuffer);
    printf("Fetched output: (%s)", outputBuffer);
    return 0;
}

ข้อมูลอ้างอิง: http://kaskavalci.com/redirecting-stdout-to-array-and-restoring-it-back-in-c/

person LWolf    schedule 19.02.2020

ฉันไม่รู้ว่าเป็นไปได้หรือไม่ที่จะได้สิ่งที่พิมพ์ครั้งล่าสุด แต่ถ้าคุณควบคุมสภาพแวดล้อม ก่อน ฟังก์ชันทดสอบของคุณจะถูกเรียกใช้ คุณสามารถเปลี่ยนเส้นทางที่เอาต์พุตมาตรฐานไป ซึ่งช่วยให้คุณเขียนมันลงใน ไฟล์ซึ่งคุณสามารถตรวจสอบได้

ดูคำตอบเก่านี้ ซึ่ง IMO ถูกละเลย ตัวอย่างจากมันถูกแก้ไขที่นี่:

FILE *fp_old = stdout;  // preserve the original stdout
stdout = fopen("/path/to/file/you/want.txt","w");  // redirect stdout to anywhere you can open
// CALL YOUR FUNCTION UNDER TEST HERE
fclose(stdout);  // Close the file with the output contents
stdout=fp_old;  // restore stdout to normal

// Re-open the file from above, and read it to make sure it contains what you expect.
person Kevin Anderson    schedule 19.02.2020
comment
ทำไมไม่เพียงแค่ freopen? - person Ruslan; 19.02.2020
comment
@Ruslan ฉันไม่แน่ใจว่า freopen() จะคงกระแสเอาต์พุตมาตรฐานดั้งเดิมไว้หรือจะปิดเอาต์พุตมาตรฐานจริง ๆ และคุณไม่สามารถนำมันกลับมาได้ (อย่างง่ายดาย) ซึ่งในสภาพแวดล้อมการทดสอบอาจไม่ดี - person Kevin Anderson; 19.02.2020
comment
@KevinAnderson ใช้ stdout เป็น l-value ไม่สามารถพกพาได้แม้ว่า ... - person CoffeeTableEspresso; 19.02.2020
comment
ยุติธรรมเพียงพอ การอภิปรายเพิ่มเติมเกี่ยวกับตัวเลือกสำหรับสิ่งนี้ได้ที่นี่: c-faq.com/stdio/undofreopen.html - person Kevin Anderson; 19.02.2020

สองทาง:

หากคุณอยู่บนระบบที่รองรับ POSIX คุณสามารถเปลี่ยนเส้นทางเอาต์พุตของโปรแกรมไปยังไฟล์โดยใช้ > จากนั้นอ่านจากไฟล์ในภายหลังเพื่อยืนยันว่าเอาต์พุตนั้นถูกต้อง

วิธีอื่นมีลักษณะดังนี้:

freopen("output.txt", "w", stdout);
f_sequence();
freopen("/dev/tty", "w", stdout);

สำหรับระบบที่รองรับ POSIX บนแพลตฟอร์มอื่น คุณจะต้องเปลี่ยน "/dev/tty" เป็นอย่างอื่น ฉันไม่ทราบถึงวิธีการพกพาที่สมบูรณ์ในการทำเช่นนี้

แล้วอ่านตั้งแต่ output.txt สิ่งที่ตัวอย่างข้างต้นทำคือเปลี่ยนสิ่งที่ stdout เป็น เพื่อให้พิมพ์เป็นไฟล์แทนที่จะเป็น stdout ปกติ

person CoffeeTableEspresso    schedule 19.02.2020
comment
ทำไมไม่เพียงแค่ freopen? - person Ruslan; 19.02.2020
comment
จริงๆ แล้ว ฉันคิดว่าคุณไม่สามารถบันทึกต้นฉบับได้ stdout ด้วยวิธีนี้... freopen จะแก้ไขพอยต์ตี - person Ruslan; 19.02.2020
comment
@Ruslan คุณพูดถูก ฉันไม่แน่ใจว่าวิธีพกพา 100% ในการทำเช่นนี้ .. - person CoffeeTableEspresso; 19.02.2020
comment
สำหรับตัวเลือกแรก ฉันจะเปลี่ยนเส้นทางเอาต์พุตไปยังไฟล์ได้อย่างไร (ฉันใช้ CLion) และฉันจะอ่านจากไฟล์เอาต์พุตด้วยโค้ดได้อย่างไร - person avivgood2; 19.02.2020
comment
ฉันอยากจะแนะนำตัวเลือกที่สองเนื่องจากตัวเลือกแรกเกี่ยวข้องกับการรวบรวมฟังก์ชันของคุณแยกจากกันและดำเนินการนั้น (เว้นแต่ฉันจะพลาดอะไรบางอย่าง) - person CoffeeTableEspresso; 19.02.2020
comment
โอ้โห ไม่สนุกเลย ฮ่าๆ ไปใช้วิธีที่สอง วิธีแรกใช้ไม่ได้กับ Windows จริงๆ - person CoffeeTableEspresso; 19.02.2020
comment
คุณใช้คอมไพเลอร์อะไร? - person CoffeeTableEspresso; 19.02.2020
comment
@CoffeeTableEspresso ในตัวเลือกที่สอง ฉันจะเอาสตริงมาจากไหน เป็นตัวแปรอะไร? - person avivgood2; 19.02.2020
comment
ผลลัพธ์ซึ่งปกติจะพิมพ์ด้วย printf จะถูกใส่ลงในไฟล์ชื่อ output.txt แทน คุณเพิ่งอ่านจากไฟล์นั้น ... - person CoffeeTableEspresso; 19.02.2020
comment
หากไฟล์ถูกบันทึกใน output.txt แล้วบรรทัด freopen("/dev/tty", "w", stdout); มีไว้เพื่ออะไร? (ขออภัยที่มีคำถามหลายข้อ ฉันแค่พยายามทำความเข้าใจโค้ด) - person avivgood2; 19.02.2020
comment
@ avivgood2 นั่นคือการคืนค่า stdout เป็นค่าเก่าหลังจากคุณทำเสร็จแล้ว เนื่องจากคุณใช้ Windows คุณอาจไม่ต้องการ /dev/tty ไม่แน่ใจว่า Windows คืออะไร... - person CoffeeTableEspresso; 19.02.2020

วิธีแก้ปัญหาหนึ่ง: คุณสามารถเขียนโปรแกรมแยกต่างหากที่รันฟังก์ชัน และในการทดสอบหน่วย คุณสามารถรันโปรแกรมนั้นเป็นกระบวนการย่อยและตรวจสอบเอาต์พุตได้ ซึ่งสามารถทำได้ด้วย std::system แต่ระวังอย่าส่งอินพุตที่ไม่คงที่ไปให้ คุณไม่ต้องการช่องโหว่ของการฉีดเชลล์แม้แต่ในการทดสอบหน่วย มีฟังก์ชันเฉพาะของระบบที่หลีกเลี่ยงการใช้เชลล์ในกระบวนการย่อย

วิธีแก้ไขปัญหาอื่นซึ่งเป็นไปได้อย่างน้อยใน POSIX: แทนที่สตรีม out / err มาตรฐานด้วยตัวอธิบายไฟล์ แล้วอ่านไฟล์ในภายหลัง

เฉพาะการทดสอบของ Google: ดูเหมือนว่าจะมี testing::internal::CaptureStdout ซึ่งดูเหมือนว่าจะใช้แนวคิดในการแทนที่สตรีมมาตรฐาน แต่ตามที่เนมสเปซบอกเป็นนัย นี่ไม่ใช่ API อย่างเป็นทางการ ดังนั้นอาจมีการเปลี่ยนแปลงในอนาคต

person eerorika    schedule 19.02.2020
comment
ฉันและเควินได้แนะนำทั้งสองตัวเลือกนี้แล้ว... - person CoffeeTableEspresso; 19.02.2020
comment
system(3) เป็นวิธีมาตรฐานในการดำเนินการกระบวนการย่อย - person Ruslan; 19.02.2020
comment
@Ruslan ฉันได้เรียนรู้ที่จะไม่คำนึงถึงมันเพราะมันไม่ปลอดภัยโดยธรรมชาติ แต่ฉันคิดว่านั่นไม่สำคัญที่นี่ตราบใดที่ไม่มีการป้อนข้อมูลจากผู้ใช้ - person eerorika; 19.02.2020
comment
@CoffeeTableEspresso ขอแสดงความยินดีกับการเป็นคนพิมพ์ดีดเร็ว - person eerorika; 19.02.2020

มีวิธีแก้ไข (ใน C ) สำหรับการเรียก API ( cmd_rsp ) ด้วยซอร์สโค้ดที่นี่ ซึ่งเมื่อเรียกใช้ในโปรแกรมของคุณ จะสร้างกระบวนการแยกต่างหาก และไปป์ไปยังทั้ง stdin และ stdout ซึ่งยอมรับคำสั่งและส่งกลับการตอบสนองผ่านบัฟเฟอร์การปรับขนาดอัตโนมัติ แนวคิดคล้ายกับ popen(...)

กรณีการใช้งานที่เรียบง่าย:

char *buf = NULL;

/// test cmd_rsp
buf = calloc(BUF_SIZE, 1);
if(!buf)return 0;
if (!cmd_rsp("dir /s", &buf, BUF_SIZE))//note the first argument can be any legal command that 
                                       //can be sent via the CMD prompt in windows, 
                                       //including a custom executable
{
    printf("%s", buf);
}
else
{
    printf("failed to send command.\n");
}
free(buf);
person ryyker    schedule 19.02.2020