// Exercise the production Camera with simulated capture and no GPU or camera. #include "camera.hpp" #include "uyvy_converter.hpp" #include #include #include #include #include #include #include #include #ifdef TEST_V4L2_CAPTURE #include "v4l2_capture.h" #else #include #include #include #include #include #endif namespace { std::string events; std::size_t cursor{}; char current{}; unsigned sequence{}; int timeoutMs{}; bool realWait{}; std::function onStop; unsigned char pixels[8]{}; void check(bool condition,char const* message) { if(!condition)throw std::runtime_error(message); } void script(std::string value) {events=std::move(value);cursor=0;} char next() { check(cursor(Impl{w,h})){} UyvyConverter::~UyvyConverter()=default; std::shared_ptr UyvyConverter::convert(void const*,std::size_t) { auto image=std::make_shared();image->width=impl_->width;image->height=impl_->height; image->stride=image->width*3;image->pixels.resize(image->stride*image->height);return image; } } #ifdef TEST_V4L2_CAPTURE V4l2Capture::~V4l2Capture()=default; bool V4l2Capture::open(std::string const&){reconnect_needed_=false;return true;} void V4l2Capture::close(){} bool V4l2Capture::setFormat(uint32_t w,uint32_t h,uint32_t p){width_=w;height_=h;pixelformat_=p;return true;} bool V4l2Capture::setFps(uint32_t){return true;} bool V4l2Capture::initMmap(uint32_t){return true;} bool V4l2Capture::startStreaming(){return true;} void V4l2Capture::releaseFrame(Frame const&){} bool V4l2Capture::getFrame(Frame& f,int waitMs) { check(waitMs<=100,"Stop polling should remain at most 100 ms"); if(next()=='s'){onStop();return false;} if(current=='d'){reconnect_needed_=true;return false;} if(current=='e'||current=='x'){return false;} if(current=='t') { // Each scripted timeout represents a full logical capture window. // The separate stop test exercises the short-wait cancellation path. std::this_thread::sleep_for(std::chrono::milliseconds(timeoutMs));return false; } check(current=='f',"Unsupported mock V4L2 event"); f.data=pixels;f.size=sizeof(pixels);f.width=2;f.height=2;f.bytes_per_line=4;f.sequence=sequence++; return true; } #else extern "C" { int __real_poll(pollfd*,nfds_t,int); int __real_open(char const*,int,...); int __real_close(int); int __real_ioctl(int,unsigned long,...); void* __real_mmap(void*,size_t,int,int,int,off_t); int __real_munmap(void*,size_t); int __wrap_open(char const* path,int flags,...) { if(std::strcmp(path,"/mock/camera")==0)return 100000; return __real_open(path,flags); } int __wrap_close(int fd){return fd==100000?0:__real_close(fd);} void* __wrap_mmap(void* a,size_t n,int p,int f,int fd,off_t o) { return fd==100000?pixels:__real_mmap(a,n,p,f,fd,o); } int __wrap_munmap(void* a,size_t n){return a==pixels?0:__real_munmap(a,n);} int __wrap_ioctl(int fd,unsigned long request,...) { va_list args;va_start(args,request);void* p=va_arg(args,void*);va_end(args); if(fd!=100000)return __real_ioctl(fd,request,p); switch(request) { case VIDIOC_QUERYCAP:static_cast(p)->capabilities=V4L2_CAP_VIDEO_CAPTURE|V4L2_CAP_STREAMING;break; case VIDIOC_S_FMT:static_cast(p)->fmt.pix.bytesperline=4;break; case VIDIOC_G_PARM:break; case VIDIOC_REQBUFS:static_cast(p)->count=2;break; case VIDIOC_QUERYBUF:static_cast(p)->length=sizeof(pixels);break; case VIDIOC_DQBUF: { if(current=='x'){errno=EIO;return -1;} auto& b=*static_cast(p);b.index=0;b.bytesused=sizeof(pixels);b.sequence=sequence++; b.flags=current=='e'?V4L2_BUF_FLAG_ERROR:0;break; } case VIDIOC_QBUF:case VIDIOC_STREAMON:case VIDIOC_STREAMOFF:break; default:throw std::runtime_error("Unexpected camera ioctl"); } return 0; } int __wrap_poll(pollfd* fds,nfds_t count,int waitMs) { if(count==1)return __real_poll(fds,count,waitMs); check(count==2&&waitMs>0&&waitMs<=timeoutMs,"Camera must respect the configured timeout window"); switch(next()) { case 't':if(realWait)std::this_thread::sleep_for(std::chrono::milliseconds(waitMs));return 0; case 'i':errno=EINTR;return -1; case 's':onStop();return 0; case 'd':fds[0].revents=POLLHUP;return 1; case 'f':case 'e':case 'x':fds[0].revents=POLLIN;return 1; default:throw std::runtime_error("Unexpected poll event"); } } int __wrap___poll_chk(pollfd* fds,nfds_t count,int waitMs,size_t size) { check(size>=count*sizeof(pollfd),"Invalid fortified poll buffer size"); return __wrap_poll(fds,count,waitMs); } } #endif int main(int argc,char** argv)try { cms::CameraConfig config;config.device="/mock/camera";config.width=config.height=2;config.buffers=2; check(config.timeout_ms==2000,"Default per-attempt timeout must be 2 seconds"); realWait=argc>1&&std::string(argv[1])=="--wall-clock"; if(!realWait)config.timeout_ms=100; timeoutMs=config.timeout_ms; if(realWait) { cms::Camera camera(config);script("tttttt"); auto before=std::chrono::steady_clock::now();expectTimeout(camera); auto elapsed=std::chrono::duration(std::chrono::steady_clock::now()-before).count(); check(cursor==6&&elapsed>=12.0&&elapsed<15.0,"Default timeout should fail after six attempts, about 12 seconds"); std::cout<<"PASS wall-clock six-timeout duration="<