2010-03-11 4 views
0

중요한 클래스와 잠금을 관리해야하는 간단한 클래스를 찾고 있는데,이를 테스트 사례로 다뤄보고 싶습니다. 이것이 의미가 있으며, 어떻게해야 할 것입니까? 클래스 작업을 확인하는 유일한 방법은 매우 복잡한 스레딩 시나리오를 설정하는 것이므로 Win32에서 중요한 섹션의 누수를 테스트하는 좋은 방법이 아니기 때문에 어렵습니다. 올바르게 작동하는지 직접 확인하는 방법이 있습니까? 여기 단위 테스트 Refcounted Critical Section 클래스

코드입니다 :

CriticalSection.hpp :

#pragma once 
#include <windows.h> 
#include <boost/shared_ptr.hpp> 

namespace WindowsAPI { namespace Threading { 

    class CriticalSectionImpl; 
    class CriticalLock; 
    class CriticalAttemptedLock; 

    class CriticalSection 
    { 
     friend class CriticalLock; 
     friend class CriticalAttemptedLock; 
     boost::shared_ptr<CriticalSectionImpl> impl; 
     void Enter(); 
     bool TryEnter(); 
     void Leave(); 
    public: 
     CriticalSection(); 
    }; 

    class CriticalLock 
    { 
     CriticalSection &ref; 
    public: 
     CriticalLock(CriticalSection& sectionToLock) : ref(sectionToLock) { ref.Enter(); }; 
     ~CriticalLock() { ref.Leave(); }; 
    }; 

    class CriticalAttemptedLock 
    { 
     CriticalSection &ref; 
     bool valid; 
    public: 
     CriticalAttemptedLock(CriticalSection& sectionToLock) : ref(sectionToLock), valid(ref.TryEnter()) {}; 
     bool LockHeld() { return valid; }; 
     ~CriticalAttemptedLock() { if (valid) ref.Leave(); }; 
    }; 

}} 

CriticalSection.cpp :

#include "CriticalSection.hpp" 

namespace WindowsAPI { namespace Threading { 

class CriticalSectionImpl 
{ 
    friend class CriticalSection; 
    CRITICAL_SECTION sectionStructure; 
    CriticalSectionImpl() { InitializeCriticalSection(&sectionStructure); }; 
    void Enter() { EnterCriticalSection(&sectionStructure); }; 
    bool TryEnter() { if (TryEnterCriticalSection(&sectionStructure)) return true; else return false; }; 
    void Leave() { LeaveCriticalSection(&sectionStructure); }; 
public: 
    ~CriticalSectionImpl() { DeleteCriticalSection(&sectionStructure); }; 
}; 

void CriticalSection::Enter() { impl->Enter(); }; 
bool CriticalSection::TryEnter() { return impl->TryEnter(); }; 
void CriticalSection::Leave() { impl->Leave(); }; 
CriticalSection::CriticalSection() : impl(new CriticalSectionImpl) {} ; 

}} 

답변

4

은 다음 세 가지 옵션이며, 개인적으로 마지막을 선호 ...

  • 귀하의 생성자에 전달할 수있는 '중요한 섹션 팩토리'인터페이스. 여기에는 사용할 API 레벨 함수를 감싸는 함수가 있습니다. 그런 다음이 인터페이스를 모의하고 테스트중인 코드에 모의 객체를 전달하면 올바른 API 함수가 호출되었는지 확인할 수 있습니다. 일반적으로이 인터페이스를 사용하지 않고 대신 API에 직접 호출 된 팩토리의 정적 인스턴스로 자체를 초기화하는 생성자도 있습니다. 객체의 일반적인 생성은 영향을받지 않지만 (기본 구현을 사용함에 따라) 테스트 할 때 계측 할 수 있습니다. 이것은 표준 의존성 주입 경로이며 결과는 parameterise from above입니다. 이 모든 단점은 당신이 간접적 인 계층을 가지고 있고 각 인스턴스에 팩토리에 대한 포인터를 저장해야한다는 것입니다 (그래서 당신은 아마도 공간과 시간 모두에서 손실 될 것입니다).
  • 또는 아래에서 API를 조롱 해 볼 수도 있습니다 ... 오래 전 API 후킹을 통해 이러한 종류의 저수준 API 사용을 테스트했습니다. 실제 Win32 API 호출을 연결했다면 더 일반적인 모의 객체와 같은 방법으로 사용되지만 위의 매개 변수보다는 "매개 변수 아래"에 의존하는 '모의 API 계층'을 개발할 수 있다는 아이디어가있었습니다. 이 방법이 효과가 있었고 프로젝트에 많은 도움이되었지만 테스트중인 코드를 조롱하는 것은 매우 복잡했습니다. 이 접근법에 대한 좋은 점은 테스트에서 제어 된 조건 하에서 API 호출이 실패 할 수 있다는 것입니다. 이것은 내가 운동을하기가 매우 어려운 실패 경로를 시험 할 수있게 해주었습니다.
  • 세 번째 방법은 일부 코드를 합리적인 리소스로 테스트 할 수 없으며 종속성 삽입이 항상 적합한 것은 아니라는 것을 받아들입니다. 가능한 한 간단하게 코드를 작성하고, 볼 수 있으며, 할 수있는 비트에 대한 테스트를 작성하고 이동할 수 있습니다. 이것은 내가 이런 상황에서하는 경향이 있습니다. 그러나

....

나는 당신의 디자인 선택의 모호한입니다. 첫째로 수업 중에 너무 많이 진행되고 있습니다 (IMHO). 참조 카운팅과 잠금은 직교합니다. 나는 중요한 부분 관리를 한 간단한 클래스를 가지기 위해 그것들을 갈라서 나눌 것이다. 그리고 내가 참조 카운팅을 정말로 필요로한다는 것을 발견했다 ... 두 번째로 참조 카운팅과 락 함수의 디자인이있다. dtor에서 잠금을 해제하는 객체를 반환하는 대신 범위가 설정된 잠금을 만들기 위해 스택에 객체를 생성하는 것만으로는 안됩니다. 이것은 많은 복잡성을 제거합니다. 사실이 정도로 간단 중요한 부분 클래스를 끝낼 수 : 심플 코드의 이러한 종류의 내 생각과 맞는

CCriticalSection::CCriticalSection() 
{ 
    ::InitializeCriticalSection(&m_crit); 
} 

CCriticalSection::~CCriticalSection() 
{ 
    ::DeleteCriticalSection(&m_crit); 
} 

#if(_WIN32_WINNT >= 0x0400) 
bool CCriticalSection::TryEnter() 
{ 
    return ToBool(::TryEnterCriticalSection(&m_crit)); 
} 
#endif 

void CCriticalSection::Enter() 
{ 
    ::EnterCriticalSection(&m_crit); 
} 

void CCriticalSection::Leave() 
{ 
    ::LeaveCriticalSection(&m_crit); 
} 

충분히 복잡한 테스트를 도입하기보다는 안구에 ...

당신은 다음과 같은 범위의 잠금 클래스를 할 수 :

CCriticalSection::Owner::Owner(
    ICriticalSection &crit) 
    : m_crit(crit) 
{ 
    m_crit.Enter(); 
} 

CCriticalSection::Owner::~Owner() 
{ 
    m_crit.Leave(); 
} 

당신은 내 코드 TryEnter()를 사용하거나 복잡한 아무것도하지 않습니다 물론이

void MyClass::DoThing() 
{ 
    ICriticalSection::Owner lock(m_criticalSection); 

    // We're locked whilst 'lock' is in scope... 
} 

처럼 사용하는 거라고하지만, 간단한 RAII 수업을 더 이상 할 수 없도록하는 것은 없습니다. 그래도 IMHO, 나는 거의가 실제로는 거의 필요하지 않다고 생각한다.

+0

몇 가지 문제가 있습니다. #1. CRITICAL_SECTION 구조는 이동하거나 복사 할 수 없습니다. 그것이 처음부터 참조 횟수를 늘리는 이유입니다. 그러므로 클라이언트가 클래스의 메모리 관리에 대해 걱정할 필요가 없기 때문에 여기에서 다시 계산하는 것이 필수적입니다. # 2 : 자물쇠 취급이 훨씬 낫습니다. 고맙습니다. # 3 : API 함수를 조롱하는 방법을 살펴 보겠습니다. 중요한 부분 객체의 절반 정도가 코드를 사용하여 코드를 테스트 할 수 있기 때문에 아이러니합니다. –

+0

나는 # 1이 문제가되는 것을 결코 발견하지 못했다. 아마 그것은 내가 자물쇠를 사용하는 방법 일뿐입니다. 단순히 자신의 영역을 잠그고 RAII '소유자'클래스를 사용하여 잠금 수명을 관리 할 수 ​​있어야하는 클래스에 'CCriticalSection'인스턴스를 추가하기 만하면됩니다. 자체 잠금을 가진 객체의 경우 거의 사본 또는 할당 op가 없습니다. 그것은 단지 의미가 없기 때문에 문제가 없으며 참조 카운트가 필요하지 않습니다. –

+0

중요한 섹션 객체를 복사 불가능하게 만들 수 있다고 생각하지만 지금은 실제로 필요한 시나리오에 있습니다. 임계 섹션 객체는 "shared_ptr"의미를 갖는다. 중요한 부분에 특별한 해체가 필요하지 않다면 나는'shared_ptr'을 사용하여 끝내 겠지만 불행히도 그들은 다음과 같이합니다 : ( –

관련 문제