2016-07-17 5 views
0

Ich möchte Unit-Test eine C-Funktion, die auf die Stdout druckt, aber nachdem ich suchte erreichte ich os.dup, os.pip und andere Sachen, die nicht die coolste Möglichkeit ist, die Stdout zu erfassen einer C-Shared-Library-Funktion. also habe ich mir gedacht, Pythons stdout an die C-Funktion zu übergeben, nachdem es darauf geschrieben hat, dann kann ich die Werte testen, aber es funktioniert nicht. Hier ist der Code:übergeben Python Stdout zu einer C-Funktion, die schreiben

die Datei als gemeinsam genutzte Bibliothek kompilieren: gcc -shared -Wl,-soname,tomat -o tomat.so -fPIC tomat.c

/* filename: tomat.c */ 
#include <stdio.h> 
#include <unistd.h> 

int 
tomat(int length, FILE *stdout_) 
{ 
    int remain = 0; 
    while ((remain = (length -= 1)) != 0) 
    { 
     fprintf(stdout_, "00:%d\n", remain); 
     sleep(1); 
    } 
    return 0; 
} 

# filename: tomat_test.py 
import sys 
import ctypes 
import unittest 

class TomatTestCase(unittest.TestCase): 
    def setUp(self): 
     self.lib = ctypes.CDLL('./tomat.so') 

    def test_prints_every_second(self): 
     seconds = ['00:1', '00:2', '00:2', '00:3', '00:4', '00:5', 
        '00:6', '00:7', '00:8', '00:9'] 
     self.lib.tomat(10, sys.stdout.fileno()) 
     self.assertEqual(output, seconds[::-1]) 
+0

Sie Sie müssen keine Datei an die C-Funktion übergeben. Es gibt kein "python stdout". Die einzige Konstante ist der gesamte Prozess. Schreib einfach was immer du brauchst an C 'stdout'. –

+0

also sollte ich die stdout erfassen? –

+1

Eine Funktion, die auf stdout druckt, sollte die C-Datei mit dem Namen 'stdout' verwenden, nicht von außen. Wenn Sie lesen möchten, was Ihre Funktion gedruckt hat, müssen Sie ** 'stdout' umleiten (ohne die Funktion zu ändern, die immer noch' stdout' verwenden soll) und dann ** den umgeleiteten Stream lesen **. –

Antwort

0

Hoffe, dass es anderen helfen würde.

import os 
import sys 
from tempfile import TemporaryFile 
from io import TextIOWrapper, SEEK_SET 
from contextlib import contextmanager 
from ctypes import c_void_p 


@contextmanager 
def capture(stream, libc): 
    osfd = sys.stdout.fileno() 
    fd = os.dup(osfd) 
    try: 
     tfile = TemporaryFile(mode='w+b') 
     redirect_stdout(tfile.fileno(), osfd, libc) 
     yield 
     redirect_stdout(fd, osfd, libc) 
     tfile.flush() 
     tfile.seek(0, SEEK_SET) 
     stream.write(tfile.read()) 
    finally: 
     tfile.close() 
     os.close(fd) 


def redirect_stdout(fd, osfd, libc): 
    libc.fflush(c_void_p.in_dll(libc, 'stdout')) 
    sys.stdout.close() 
    os.dup2(fd, osfd) 
    sys.stdout = TextIOWrapper(os.fdopen(osfd, 'wb')) 

Wie habe ich es den Ausgang des Timers funciton zu testen:

from io import BytesIO 
from ctypes import CDLL 
from unittest import TestCase 
from helpers import capture 


class TomatTestCase(TestCase): 
    def setUp(self): 
     self.libc = CDLL('./tomat.so') 
     self.maxDiff = None 

    def test_prints_every_second(self): 
     seconds = [ 
      '01:00', '01:01', '01:02', '01:03', '01:04', '01:05', '01:06', 
      '01:07', '01:08', '01:09', '01:10', '01:11', '01:12', '01:13', 
      '01:14', '01:15', '01:16', '01:17', '01:18', '01:19', '01:20', 
      '01:21', '01:22', '01:23', '01:24', '01:25', '01:26', '01:27', 
      '01:28', '01:29', '01:30', '01:31', '01:32', '01:33', '01:34', 
      '01:35', '01:36', '01:37', '01:38', '01:39', '01:40', '01:41', 
      '01:42', '01:43', '01:44', '01:45', '01:46', '01:47', '01:48', 
      '01:49', '01:50', '01:51', '01:52', '01:53', '01:54', '01:55', 
      '01:56', '01:57', '01:58', '01:59', '01:60'] 

     stream = BytesIO() 
     with capture(stream, self.libc): 
      self.libc.tomat(2) 
     stream = stream.getvalue().split() 
     output = [byte.decode() for byte in stream] 

     self.assertListEqual(output, seconds) 

für weitere Informationen darüber, wie es funktioniert, wie Sie einen Blick auf die Post Eli Bendersky nehmen: http://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/