VisualUnitTest++: Native Code라서 겪는 문제

계속해서 VisualUnitTest++을 사용 중. (이하 VUTPP라고 부르겠음)

내가 이해하는 작동 구조가,

  1. Compiler.Preprocessor.Definition[1] 에 특정 값(VUTPP_UNITTEST++)이 있는지 확인하고 있으면,
  2. 해당 프로젝트의 파일들을 해석해서 SUITE( ), TEST() 매크로가 있나 확인하고,
  3. 전체 테스트를 실행하거나, 선택한 테스트를 실행하면,
  4. (빌드가 필요하면 빌드를 하고) VS AddIn으로 떠있던 VUTPP에서 DLL 파일을 링크 & 실행
  5. 실행 중에 적절한 callback을 호출해서 결과값을 받음
  6. 최종적으로 결과값(성공/실패여부 등등) 표시

인데, 4에 약간의 문제?가 있는 것 같다.

테스트되는 코드가 native C++이라 생기는 어쩔 수 없는 문제에 가까운 것인데.

  • VUTPP는 VS AddIn이니 VisualStudio의 프로세스 공간에서 동작한다
  • 테스트는 DLL파일을 링크해서 실행되는데 이 것은 VUTPP가 실행하는 것이다
  • 테스트되는 DLL안에서 잘못된 메모리를 참조한다거나 하면 VUTPP 사망 → VisualStudio 사망 으로 연쇄 반응(?)

이라는 것. .Net managed code나 python, Java같은 언어에서라면 exception을 처리하는 것만으로 충분할텐데, 실패하면 OS가 직접처리(보통은 프로그램 사망으로 결론)하는 C++은 괴롭구나 Orz
아마 이것 때문에 VUTPP 제작자인 쑥갓님도 “VS자체가 죽을 수 있습니다”라고 써놓으신 것 같은데…

어제 테스트하던 라이브러리에서 지운 메모리를 참조하는 버그가 있어서 좀 괴로웠다(…). UnitTest 돌릴 때마다 VS가 사망…

아마 이런 해결책이 가능하지 않을까싶다. 문제가 되는게 VS AddIn과 믿을 수 없는 테스트될 DLL이 강하게 결합되어 있다라는 것. 이걸 좀 더 약한 결합으로 바꿀 수 있지 않을까?

  • 별도의 “실행기”를 만들고, 이걸 command line으로 호출한다. 결과값은 문자열같은 걸로 받아서 VUTPP가 재해석
  • 별도의 “실행기”를 만들고, 이걸 socket으로 연결한다(일종의 RPC?). 그리고 결과값을 소켓 통신으로 받아서 VUTPP가 재해석

즉, “별도의 실행기”와 VUTPP를 분리하고 이를 약한 결합인 command line + text나 socket 통신으로 연결해보자는 것[2]

주말에 시간나면 짜볼까? (시간나면보다는 “엄한 일로 시간을 낭비하지 않으면”에 가깝긴하지만…)

  1. Project Property → C/C++ → Preprocessor Definitions에 해당 []
  2. 통신이 실패하거나 여하튼 죽어버리면 JUnit의 failure(비정상 종료 등등)처럼 처리해버리면 되나? []

Published by

rein

나는 ...

14 thoughts on “VisualUnitTest++: Native Code라서 겪는 문제”

  1. 기본적으로 Unhandled Exception처리는 하고 있어서 잡히는것들도 꽤 있고, UnitTest++의 경우는 자체실행부분에도 Exception처리가 있어서 Exception이 나는경우는 따로 Failure로 처리를 해줍니다.
    하지만 이런것들은 런타임에러의 경우는 소용이 없고, 런타임에러의 경우는 통상적은 Exception뿐만 아니라 SEH로도 처리가 불가능하므로.. 흠좀 곤란한 문제가 있습니다.
    별도의 실행기를 만들고 결과를 받는 방법은 Failure레포팅 받기도 힘들고, 내부에서 죽은것지 아니면 끝나지 않는건지-_- 판별이 안되기때문에 좀 곤란한 문제가 있군요.
    그렇다고 여기다가 정말 소켓통신;;까지 넣기는 흠좀;;;
    현재 작업중인 버전이 끝나고 나면 한번 고민해 보겠습니다;
    좋은 생각 나시면 말씀해주세요^^

  2. 실제로 UnitTest에
    int *p = 0;
    *p = 3;
    라는 코드를 테스트 하면
    Unhandled exception: Crash!
    라고 Failure가 결과로 보입니다.

  3. 실제로 겪은 것은
    some_class p* = (0xcdcdcdcd); (삭제된 메모리)
    에 대해서
    ( *( p->function_pointer_table[ some_index ] ))( some_arg, … );
    을 호출하는 코드였는데, 이건 그냥 VS가 통채로 죽더라고요; (잘못된 메모리 주소에 있는 함수 포인터 테이블을 참조해서 코드를 호출)

    + abort() 를 호출해도 VS님이 같이 사망하는 비극(?)을 보고있습니다;

  4. 저런 경우는 죽을것 같긴 합니다.;;
    혹시 시간이 괜찮으시면 죽는경우에 대한 샘플프로젝트를 만들어 주시면 다음다음 버전만들때 참고하도록 하겠습니다^^

  5. 아 죄송합니다. 저경우에 죽는다고 생각했는데 저 경우를 방지하기 위한 매크로에서 죽었네요 -_-;; (해당 경우 비슷한 상황에서 abort())

    결론: abort()를 사용하는 경우에는 죽는다네요. (abort(), assert( false ) … )
    코드 내부에 있는 assert를 다 뺄 수도 없고 좀 골치아프긴하네요;

    + STL 디버깅 모드에서 (물론 제 프로그래밍 실수지만) assertion–failure가 걸리기도 하네요 -_-;; 이 경우는 좀 난감하군요; assert를 뺼 수도 없고;

  6. 생각해보니 프로그래밍 대회 류의 채점 프로그램과 통하는 점도 있네요. 매번 채점시마다 프로세스 새로 만들고 그게 OK응답 fail 응답 그냥 프로세스가 죽어버리기 등으로 나눠서 확인한다거나 할 수도 있겠네요.

  7. ipkn / 그쪽이 가장 유사(?)한 응용일 것 같네. Rica나 탱이한테 좀 물어볼까…

    그러고보니 ESCamp CodeRace때는 어찌했냐

  8. 흠 그거라면,

    1. dll로 만드는 걸 유지하고
    2. 그걸 띄우는 애를 만들고(dll 링크 & 실행)
    3. 원래 직접 링크해서 실행하던 애는 2랑 통신하면 되는건가

    근데 그게 기존방법이긴하다.(CC.Net에서 쓰던)
    지금 겪는건 VS AddIn에서 abort() 받으면 대책없이 죽어버리는 문제고(VS가).
    프로세스를 빼는게 해결책인건가;

  9. Pingback: rein's world
  10. 음_-_Sample Project를 import해서 빌드 성공했는데 Addin 에 Sample 들이 나타나지 않으면 멀 빠뜨린 걸까요 T.T

    Rein 님의 답글을 애타게 기다려보며 … ;;;

    // 저자 분의 홈피에도 글을 남기긴 했다만 중뷁으로 또 남겨보아요~ ㅋㅋ

  11. 위 내용 반복이긴하지만;

    • DLL로 내보내는 녀석의 Preprocess 정의에 VUTPP_UNITTEST++ 이 있나 보시고
    • DLL쪽에서 외부로 내보내는 함수가 예제에 있는 거랑 같은 형태인지 보시고
    • 마지막으로(…) Solution Filter를 만들어서 쓰고있진 않은지(필터에 들어있으면 외부에 안보임) 확인해보시면

    되지 않을까 합니다;

  12. 흠. Preprocessor 정의에 있고 필터는 쓰지 않고 있으니 DLL export의 문제 인 것 같은데
    평상시 def를 사용하고 있어서 맨날 하던 것처럼 def에 RunTest만 추가한게 먼가 틀렸나보네요;;

    음 TestApp.cpp 이나 .h 를 어떻게 수정하는지 초보에게 설명을 좀 T.T 부탁드려도 될까요;;;

    // 프로그래밍은 이런게 어려워요 T.T

  13. 제가 댓글을 제대로 안 읽고 답을 한 것 같은데요 -_-;;

    * 샘플 프로젝트(VUTPP 홈페이지에서 제공하는) 에서 안나타났고
    * 임포트 했다

    라는 이야기는 혹시 VS 2008이신건가요? 아직 VS 2008이 제대로 지원되는건 아니었던 것 같습니다; 애드인을 VS 2008용으로 포팅해야하는 문제라;

Leave a Reply