85 lines
5.6 KiB
C++
85 lines
5.6 KiB
C++
// Interactive clipboard test. Run with the resident stopped; the original
|
|
// clipboard is restored. The helper must be beside this test executable.
|
|
#include "storage.h"
|
|
#include "ocr.h"
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
static void require(bool ok,const char *name) {
|
|
if(!ok) throw std::runtime_error(name);
|
|
std::cout << "PASS " << name << '\n';
|
|
}
|
|
int main() {
|
|
CoInitializeEx(nullptr,COINIT_MULTITHREADED);
|
|
HWND owner=CreateWindowExW(0,L"STATIC",L"Capture test",0,0,0,0,0,HWND_MESSAGE,nullptr,nullptr,nullptr);
|
|
ks::Clip saved; bool captured=false; int result=0;
|
|
auto folder=std::filesystem::temp_directory_path()/(L"KSwitcher-image-capture-"+std::to_wstring(GetCurrentProcessId()));
|
|
try {
|
|
saved=ks::readClipboard(owner,128*1024*1024); captured=true;
|
|
ks::Settings settings; settings.history=true;
|
|
ks::History history(folder);
|
|
history.add(ks::textClip(L"keep pinned"),L"test",settings);
|
|
auto pinned=history.entries().front().id; history.pin(pinned);
|
|
ks::Pixels pixels{32,16,ks::Bytes(32*16*4,255)};
|
|
ks::Clip image{{ks::imageDib(pixels),{0,L"PNG",ks::encodePng(pixels)}}};
|
|
auto sequence=ks::writeClipboard(owner,image);
|
|
require(history.captureImage(sequence,L"capture test",settings),"helper captures image");
|
|
auto entries=history.entries();
|
|
require(entries.size()==2 && entries.front().image && entries.front().ocrState==1,"resident reloads metadata with pending OCR");
|
|
require(entries.front().contentHash.size()==64 && !entries.front().pixelHash.empty(),
|
|
"helper persists fingerprints and resident reloads them");
|
|
require(entries.back().id==pinned && entries.back().pinned,"helper preserves existing pinned entry");
|
|
auto restored=history.load(entries.front().id);
|
|
auto thumbnail=history.thumbnail(entries.front().id);
|
|
require(thumbnail.width==pixels.width && thumbnail.height==pixels.height && thumbnail.bgra==pixels.bgra,
|
|
"helper thumbnail preserves small image pixels");
|
|
bool thumbnailCancelled=false;
|
|
try {history.thumbnail(entries.front().id,[]{return true;});} catch(...) {thumbnailCancelled=true;}
|
|
require(thumbnailCancelled,"closed history cancels thumbnail work");
|
|
auto has=[&](const ks::Format &expected) {
|
|
return std::any_of(restored.formats.begin(),restored.formats.end(),[&](const ks::Format &actual) {
|
|
return (expected.name.empty()?actual.id==expected.id:actual.name==expected.name) && actual.data==expected.data;
|
|
});
|
|
};
|
|
require(has(image.formats[0]) && has(image.formats[1]),
|
|
"encrypted history preserves both image representations");
|
|
require(ks::readFile(folder/(entries.front().id+L".dat"),1024*1024)!=ks::encodeClip(restored),"saved image is encrypted");
|
|
require(GetClipboardSequenceNumber()==sequence,"capture leaves clipboard unchanged");
|
|
require(history.captureImage(sequence,L"capture test",settings) && history.entries().size()==2,"duplicate capture is not added");
|
|
ks::writeClipboard(owner,ks::textClip(L"new clipboard"));
|
|
require(!history.captureImage(sequence,L"stale",settings) && history.entries().size()==2,"stale request does not save another clipboard");
|
|
for(auto name : {L"ExcludeClipboardContentFromMonitorProcessing",L"Clipboard Viewer Ignore",L"CanIncludeInClipboardHistory"}) {
|
|
auto blocked=image; blocked.formats.push_back({0,name,ks::Bytes(4,0)});
|
|
sequence=ks::writeClipboard(owner,blocked);
|
|
require(!history.captureImage(sequence,L"excluded",settings) && history.entries().size()==2,"clipboard privacy flag prevents capture");
|
|
}
|
|
pixels={1024,512,ks::Bytes(1024*512*4,255)};
|
|
sequence=ks::writeClipboard(owner,ks::Clip{{ks::imageDib(pixels)}});
|
|
settings.maxImageMiB=1;
|
|
bool rejected=false;
|
|
try {history.captureImage(sequence,L"oversized",settings);} catch(const std::exception &e) {
|
|
rejected=std::string(e.what())=="Clipboard exceeds the configured size limit";
|
|
}
|
|
require(rejected && history.entries().size()==2,"oversized image is rejected without changing index");
|
|
require(GetClipboardSequenceNumber()==sequence,"rejection leaves clipboard unchanged");
|
|
settings.maxImageMiB=20;
|
|
history.add(ks::Clip{{ks::imageDib(pixels)}},L"thumbnail fixture",settings);
|
|
auto largeId=history.entries().front().id;
|
|
thumbnail=history.thumbnail(largeId);
|
|
require(thumbnail.width==208 && thumbnail.height==104 && thumbnail.bgra.size()==208*104*4,
|
|
"large image returns only bounded thumbnail pixels");
|
|
history.remove(largeId);
|
|
bool missingRejected=false;
|
|
try {history.thumbnail(largeId);} catch(...) {missingRejected=true;}
|
|
require(missingRejected,"deleted image cannot be previewed or recreated");
|
|
for(const auto &file:std::filesystem::directory_iterator(folder))
|
|
require(file.path().extension()!=L".request","encrypted capture request is cleaned up");
|
|
} catch(const std::exception &e) {std::cerr << "FAIL " << e.what() << '\n';result=1;}
|
|
if(captured) try {ks::writeClipboard(owner,saved);} catch(...) {result=1;}
|
|
DestroyWindow(owner); CoUninitialize();
|
|
// Remove only the exact PID-specific fixture under the system temp directory.
|
|
std::error_code ignored;
|
|
if(std::filesystem::weakly_canonical(folder).parent_path()==std::filesystem::weakly_canonical(std::filesystem::temp_directory_path()))
|
|
std::filesystem::remove_all(folder,ignored);
|
|
return result;
|
|
}
|