// * ok is entirely opt-in. No more maintaining huge --blacklists.
#include "SkGraphics.h"
-#include "SkOSFile.h"
#include "ok.h"
#include <chrono>
#include <future>
#include <list>
-#include <regex>
#include <stdio.h>
#include <stdlib.h>
#include <thread>
+#include <vector>
#if !defined(__has_include)
#define __has_include(x) 0
#endif
-static thread_local const char* tls_name = "";
+static thread_local const char* tls_currently_running = "";
#if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
#include <execinfo.h>
#undef CASE
}
log(" while running '");
- log(tls_name);
+ log(tls_currently_running);
log("'\n");
void* stack[128];
void ok_log(const char* msg) {
lockf(log_fd, F_LOCK, 0);
log("[");
- log(tls_name);
+ log(tls_currently_running);
log("]\t");
log(msg);
log("\n");
}
#endif
-enum class Status { OK, Failed, Crashed, Skipped, None };
-
struct Engine {
virtual ~Engine() {}
virtual bool spawn(std::function<Status(void)>) = 0;
#endif
struct StreamType {
- const char* name;
+ const char *name, *help;
std::unique_ptr<Stream> (*factory)(Options);
};
static std::vector<StreamType> stream_types;
struct DstType {
- const char* name;
+ const char *name, *help;
std::unique_ptr<Dst> (*factory)(Options);
};
static std::vector<DstType> dst_types;
struct ViaType {
- const char* name;
+ const char *name, *help;
std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>);
};
static std::vector<ViaType> via_types;
+template <typename T>
+static std::string help_for(std::vector<T> registered) {
+ std::string help;
+ for (auto r : registered) {
+ help += "\n ";
+ help += r.name;
+ help += ": ";
+ help += r.help;
+ }
+ return help;
+}
+
int main(int argc, char** argv) {
SkGraphics::Init();
setup_crash_handler();
- int jobs {1};
- std::regex match {".*"};
- std::regex search {".*"};
- std::string write_dir {""};
-
+ int jobs{1};
std::unique_ptr<Stream> stream;
- std::function<std::unique_ptr<Dst>(void)> dst_factory;
+ std::function<std::unique_ptr<Dst>(void)> dst_factory = []{
+ // A default Dst that's enough for unit tests and not much else.
+ struct : Dst {
+ Status draw(Src* src) override { return src->draw(nullptr); }
+ sk_sp<SkImage> image() override { return nullptr; }
+ } dst;
+ return move_unique(dst);
+ };
auto help = [&] {
- std::string stream_names, dst_names, via_names;
- for (auto s : stream_types) {
- if (!stream_names.empty()) {
- stream_names += ", ";
- }
- stream_names += s.name;
- }
- for (auto d : dst_types) {
- if (!dst_names.empty()) {
- dst_names += ", ";
- }
- dst_names += d.name;
- }
- for (auto v : via_types) {
- if (!via_names.empty()) {
- via_names += ", ";
- }
- via_names += v.name;
- }
+ std::string stream_help = help_for(stream_types),
+ dst_help = help_for( dst_types),
+ via_help = help_for( via_types);
printf("%s [-j N] [-m regex] [-s regex] [-w dir] [-h] \n"
" src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n"
" If <0, use -N threads instead. \n"
" If 0, use one thread in one process. \n"
" If 1 (default) or -1, auto-detect N. \n"
- " -m: Run only names matching regex exactly. \n"
- " -s: Run only names matching regex anywhere. \n"
- " -w: If set, write .pngs into dir. \n"
" -h: Print this message and exit. \n"
- " src: content to draw: %s \n"
- " dst: how to draw that content: %s \n"
- " via: front-patches to the dst: %s \n"
- " Some srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
- argv[0], stream_names.c_str(), dst_names.c_str(), via_names.c_str());
+ " src: content to draw%s \n"
+ " dst: how to draw that content%s \n"
+ " via: wrappers around dst%s \n"
+ " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
+ argv[0], stream_help.c_str(), dst_help.c_str(), via_help.c_str());
return 1;
};
for (int i = 1; i < argc; i++) {
- if (0 == strcmp("-j", argv[i])) { jobs = atoi(argv[++i]); }
- if (0 == strcmp("-m", argv[i])) { match = argv[++i] ; }
- if (0 == strcmp("-s", argv[i])) { search = argv[++i] ; }
- if (0 == strcmp("-w", argv[i])) { write_dir = argv[++i] ; }
+ if (0 == strcmp("-j", argv[i])) { jobs = atoi(argv[++i]); }
if (0 == strcmp("-h", argv[i])) { return help(); }
for (auto s : stream_types) {
}
}
if (!stream) { return help(); }
- if (!dst_factory) {
- // A default Dst that's enough for unit tests and not much else.
- dst_factory = []{
- struct : Dst {
- bool draw(Src* src) override { return src->draw(nullptr); }
- sk_sp<SkImage> image() override { return nullptr; }
- } dst;
- return move_unique(dst);
- };
- }
std::unique_ptr<Engine> engine;
if (jobs == 0) { engine.reset(new SerialEngine); }
if (jobs == 1) { jobs = std::thread::hardware_concurrency(); }
- if (!write_dir.empty()) {
- sk_mkdir(write_dir.c_str());
- }
-
int ok = 0, failed = 0, crashed = 0, skipped = 0;
auto update_stats = [&](Status s) {
spawn([=] {
std::unique_ptr<Src> src{raw};
- auto name = src->name();
- tls_name = name.c_str();
- if (!std::regex_match (name, match) ||
- !std::regex_search(name, search)) {
- return Status::Skipped;
- }
-
- auto dst = dst_factory();
- if (!dst->draw(src.get())) {
- return Status::Failed;
- }
+ std::string name = src->name();
+ tls_currently_running = name.c_str();
- if (!write_dir.empty()) {
- auto image = dst->image();
- sk_sp<SkData> png{image->encode()};
- SkFILEWStream{(write_dir + "/" + name + ".png").c_str()}
- .write(png->data(), png->size());
- }
- return Status::OK;
+ return dst_factory()->draw(src.get());
});
}
}
-Register::Register(const char* name, std::unique_ptr<Stream> (*factory)(Options)) {
- stream_types.push_back(StreamType{name, factory});
+Register::Register(const char* name, const char* help,
+ std::unique_ptr<Stream> (*factory)(Options)) {
+ stream_types.push_back(StreamType{name, help, factory});
}
-Register::Register(const char* name, std::unique_ptr<Dst> (*factory)(Options)) {
- dst_types.push_back(DstType{name, factory});
+Register::Register(const char* name, const char* help,
+ std::unique_ptr<Dst> (*factory)(Options)) {
+ dst_types.push_back(DstType{name, help, factory});
}
-Register::Register(const char* name,
+Register::Register(const char* name, const char* help,
std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) {
- via_types.push_back(ViaType{name, factory});
+ via_types.push_back(ViaType{name, help, factory});
}
Options::Options(std::string str) {
* found in the LICENSE file.
*/
-#include "ok.h"
+#include "SkOSFile.h"
#include "SkPictureRecorder.h"
+#include "ok.h"
+#include <regex>
-static std::unique_ptr<Src> proxy(Src* original, std::function<bool(SkCanvas*)> fn) {
+static std::unique_ptr<Src> proxy(Src* original, std::function<Status(SkCanvas*)> fn) {
struct : Src {
- Src* original;
- std::function<bool(SkCanvas*)> fn;
+ Src* original;
+ std::function<Status(SkCanvas*)> fn;
std::string name() override { return original->name(); }
SkISize size() override { return original->size(); }
- bool draw(SkCanvas* canvas) override { return fn(canvas); }
+ Status draw(SkCanvas* canvas) override { return fn(canvas); }
} src;
src.original = original;
src.fn = fn;
return move_unique(via);
}
- bool draw(Src* src) override {
+ Status draw(Src* src) override {
SkRTreeFactory factory;
SkPictureRecorder rec;
rec.beginRecording(SkRect::MakeSize(SkSize::Make(src->size())),
rtree ? &factory : nullptr);
- if (!src->draw(rec.getRecordingCanvas())) {
- return false;
+ for (auto status = src->draw(rec.getRecordingCanvas()); status != Status::OK; ) {
+ return status;
}
auto pic = rec.finishRecordingAsPicture();
return target->draw(proxy(src, [=](SkCanvas* canvas) {
pic->playback(canvas);
- return true;
+ return Status::OK;
}).get());
}
return target->image();
}
};
-static Register via_pic{"via_pic", ViaPic::Create};
+static Register via_pic{"via_pic", "record then play back an SkPicture", ViaPic::Create};
+
+struct Png : Dst {
+ std::unique_ptr<Dst> target;
+ std::string dir;
+
+ static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) {
+ Png via;
+ via.target = std::move(dst);
+ via.dir = options("dir", "ok");
+ return move_unique(via);
+ }
+
+ Status draw(Src* src) override {
+ for (auto status = target->draw(src); status != Status::OK; ) {
+ return status;
+ }
+
+ auto image = target->image();
+ sk_sp<SkData> png{image->encode()};
+
+ sk_mkdir(dir.c_str());
+ SkFILEWStream{(dir + "/" + src->name() + ".png").c_str()}
+ .write(png->data(), png->size());
+ return Status::OK;
+ }
+
+ sk_sp<SkImage> image() override {
+ return target->image();
+ }
+};
+static Register png{"png", "dump PNGs to dir=ok" , Png::Create};
+
+struct Filter : Dst {
+ std::unique_ptr<Dst> target;
+ std::regex match, search;
+
+ static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) {
+ Filter via;
+ via.target = std::move(dst);
+ via.match = options("match", ".*");
+ via.search = options("search", ".*");
+ return move_unique(via);
+ }
+
+ Status draw(Src* src) override {
+ auto name = src->name();
+ if (!std::regex_match (name, match) ||
+ !std::regex_search(name, search)) {
+ return Status::Skipped;
+ }
+ return target->draw(src);
+ }
+
+ sk_sp<SkImage> image() override {
+ return target->image();
+ }
+};
+struct Register filter{"filter",
+ "run only srcs matching match=.* exactly and search=.* somewhere",
+ Filter::Create};