Dapatkan string terakhir dicetak dalam C/C++

Saya mencoba membuat penguji menggunakan googletest. masalahnya adalah fungsi yang saya uji mengembalikan kekosongan dan malah mencetak hasilnya. Saya ingin string terakhir dicetak ke konsol sehingga saya dapat menguji hasilnya. string mungkin berisi \n.

jadi saya punya fungsinya sendiri:

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

dan kemudian penguji:

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

Apa itu mungkin?

Fungsi yang saya uji ada di c, sedangkan fungsi Test itu sendiri (penguji) ada di c++. hasilnya dicetak menggunakan printf. Saya tidak dapat mengubah fungsinya sendiri. Saya menggunakan CLion versi terbaru.


person avivgood2    schedule 19.02.2020    source sumber
comment
Bisakah Anda mengubah fungsinya sehingga menerima parameter std::ostream& alih-alih hanya mencetak ke stdout/stderr?   -  person jtbandes    schedule 19.02.2020
comment
c atau c++? Bahasanya berbeda dan solusinya akan sangat berbeda. (dan tidak ada bahasa yang disebut C/C++, sayangnya itu adalah istilah yang keliru)   -  person 463035818_is_not_a_number    schedule 19.02.2020
comment
@idclev463035818 Fungsi yang saya uji ada di c, sedangkan fungsi Test itu sendiri (penguji) ada di c++   -  person avivgood2    schedule 19.02.2020
comment
@ avivgood2 -- posting beberapa kode (minimal) yang menunjukkan masalahnya.   -  person Happy Green Kid Naps    schedule 19.02.2020
comment
@jtbandes Dicetak menggunakan printf dan saya tidak bisa mengubahnya, tulis saja pengujinya   -  person avivgood2    schedule 19.02.2020
comment
bukan topik yang ingin saya bantu, namun Anda tetap harus memberikan contoh minimal yang dapat direproduksi. Detail memang penting   -  person 463035818_is_not_a_number    schedule 19.02.2020
comment
Anda dapat mengarahkan stdout ke file lalu membaca dari file tersebut saat fungsi C Anda kembali.   -  person Ruslan    schedule 19.02.2020
comment
Fungsi ini tidak dapat diuji sebagaimana adanya. Saya akan merekomendasikan saran @jtbandes.   -  person andre    schedule 19.02.2020
comment
Ada kode sumber DI SINI (hanya untuk aplikasi Windows) yang membuat API untuk mengirim cmd dan membaca stdout ke dalam buffer ukuran dinamis. Saya telah mengujinya hingga 2GBytes ketika membaca panggilan direktori rekursif ke Windows. ( Ini yang asli, sebelum ditinjau. )   -  person ryyker    schedule 19.02.2020


Jawaban (5)


Arahkan ulang output standar ke buffer.

Langsung di 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;
}

Referensi: http://kaskavalci.com/redirecting-stdout-to-array-and-restoring-it-back-in-c/

person LWolf    schedule 19.02.2020

Saya tidak tahu apakah mungkin untuk mendapatkan apa yang terakhir dicetak, tetapi jika Anda mengontrol lingkungan sebelum fungsi pengujian Anda dipanggil, Anda dapat mengarahkan ulang ke mana keluaran standar pergi, yang memungkinkan Anda menulisnya ke file, yang kemudian dapat Anda periksa.

Lihat jawaban lama ini yang IMO diabaikan. Contohnya diubah di sini:

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
Mengapa tidak freopen saja? - person Ruslan; 19.02.2020
comment
@Ruslan Saya tidak yakin apakah freopen() akan mempertahankan aliran keluaran standar asli, atau apakah itu benar-benar akan menutup keluaran standar, dan Anda tidak dapat mendapatkannya kembali (dengan mudah), yang mungkin buruk dalam lingkungan pengujian. - person Kevin Anderson; 19.02.2020
comment
@KevinAnderson menggunakan stdout sebagai nilai-l tidak portabel... - person CoffeeTableEspresso; 19.02.2020
comment
Cukup adil. Diskusi lebih lanjut mengenai opsi untuk hal semacam ini di sini: c-faq.com/stdio/undofreopen.html - person Kevin Anderson; 19.02.2020

Dua arah:

Jika Anda menggunakan sistem yang kompatibel dengan POSIX, Anda dapat mengarahkan output program ke file menggunakan >, lalu membaca file tersebut nanti untuk mengonfirmasi bahwa outputnya benar.

Cara lainnya adalah seperti ini:

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

untuk sistem yang kompatibel dengan POSIX. Di platform lain Anda perlu mengubah "/dev/tty" ke yang lain. Saya tidak mengetahui cara yang sepenuhnya portabel untuk melakukan ini.

Dan kemudian baca dari output.txt. Apa yang dilakukan cuplikan di atas adalah mengubah stdout itu, sehingga dicetak ke file, bukan stdout normal.

person CoffeeTableEspresso    schedule 19.02.2020
comment
Mengapa tidak freopen saja? - person Ruslan; 19.02.2020
comment
Sebenarnya, menurut saya Anda tidak dapat menyimpan stdout asli dengan cara ini... freopen akan memodifikasi pointee. - person Ruslan; 19.02.2020
comment
@Ruslan kamu benar. Namun saya tidak yakin cara portabel 100% untuk melakukan ini.. - person CoffeeTableEspresso; 19.02.2020
comment
Sedangkan untuk opsi pertama, bagaimana cara mengarahkan output ke file? (Saya menggunakan CLIon). dan bagaimana saya bisa membaca dari file keluaran melalui kode? - person avivgood2; 19.02.2020
comment
Saya akan merekomendasikan opsi kedua karena opsi pertama melibatkan kompilasi fungsi Anda secara terpisah dan menjalankannya (kecuali saya melewatkan sesuatu).. - person CoffeeTableEspresso; 19.02.2020
comment
Ooooooh, tidak semenyenangkan itu haha. Gunakan cara kedua, cara pertama tidak akan berhasil di Windows. - person CoffeeTableEspresso; 19.02.2020
comment
Kompiler apa yang Anda gunakan? - person CoffeeTableEspresso; 19.02.2020
comment
@CoffeeTableEspresso di opsi kedua, dari mana saya mendapatkan stringnya? ke dalam variabel apa? - person avivgood2; 19.02.2020
comment
output yang biasanya dicetak dengan printf malah dimasukkan ke dalam file bernama output.txt. Anda baru saja membaca dari file itu... - person CoffeeTableEspresso; 19.02.2020
comment
jika file disimpan di output.txt lalu untuk apa baris freopen("/dev/tty", "w", stdout); itu? (maaf untuk banyak pertanyaan, saya hanya mencoba memahami kodenya) - person avivgood2; 19.02.2020
comment
@avivgood2 yaitu mengembalikan stdout ke nilai lama setelah Anda selesai. Karena Anda menggunakan Windows, Anda mungkin tidak menginginkan /dev/tty. Tidak yakin apa itu Windows... - person CoffeeTableEspresso; 19.02.2020

Salah satu solusinya: Anda dapat menulis program terpisah yang menjalankan fungsi tersebut, dan dalam pengujian unit, Anda dapat menjalankan program tersebut sebagai sub-proses dan memeriksa hasilnya. Hal ini dapat dilakukan dengan std::system, namun berhati-hatilah agar tidak memberikan masukan yang tidak konstan ke dalamnya. Anda tidak ingin kerentanan injeksi shell bahkan dalam pengujian unit. Ada fungsi khusus sistem yang menghindari penggunaan shell dalam subproses.

Solusi lain, yang mungkin dilakukan setidaknya di POSIX: Ganti aliran out / err standar dengan deskriptor file, dan baca file setelahnya.

Khusus Googletest: Tampaknya ada testing::internal::CaptureStdout, yang tampaknya menerapkan gagasan mengganti aliran standar. Namun sesuai dengan namespacenya, ini bukan API resmi, jadi mungkin berubah di masa mendatang.

person eerorika    schedule 19.02.2020
comment
Saya dan Kevin sudah menyarankan kedua opsi ini... - person CoffeeTableEspresso; 19.02.2020
comment
system(3) adalah cara standar untuk menjalankan subproses. - person Ruslan; 19.02.2020
comment
@Ruslan Saya telah belajar untuk mengabaikannya karena sifatnya yang tidak aman. Tapi saya kira itu tidak penting di sini selama tidak ada masukan pengguna. - person eerorika; 19.02.2020
comment
@CoffeeTableEspresso Selamat, Anda telah menjadi juru ketik yang cepat. - person eerorika; 19.02.2020

Ada solusi ( dalam C ) untuk panggilan API ( cmd_rsp ) dengan kode sumber di sini, yang ketika dipanggil dalam program Anda, akan membuat proses terpisah, dan menyalurkan ke stdin dan stdout, yang kemudian menerima perintah dan mengembalikan respons melalui buffer ukuran otomatis. Konsepnya mirip dengan popen(...).

Kasus penggunaan sederhana:

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