joplin/CliClient/app/fuzzing.js

2403 lines
33 KiB
JavaScript

"use strict";
const { time } = require("lib/time-utils.js");
const { Logger } = require("lib/logger.js");
const Resource = require("lib/models/Resource.js");
const { dirname } = require("lib/path-utils.js");
const { FsDriverNode } = require("./fs-driver-node.js");
const lodash = require("lodash");
const exec = require("child_process").exec;
const fs = require("fs-extra");
const baseDir = dirname(__dirname) + "/tests/fuzzing";
const syncDir = baseDir + "/sync";
const joplinAppPath = __dirname + "/main.js";
let syncDurations = [];
const fsDriver = new FsDriverNode();
Logger.fsDriver_ = fsDriver;
Resource.fsDriver_ = fsDriver;
const logger = new Logger();
logger.addTarget("console");
logger.setLevel(Logger.LEVEL_DEBUG);
process.on("unhandledRejection", (reason, p) => {
console.error("Unhandled promise rejection", p, "reason:", reason);
});
function createClient(id) {
return {
id: id,
profileDir: baseDir + "/client" + id,
};
}
async function createClients() {
let output = [];
let promises = [];
for (let clientId = 0; clientId < 2; clientId++) {
let client = createClient(clientId);
promises.push(fs.remove(client.profileDir));
promises.push(
execCommand(client, "config sync.target 2").then(() => {
return execCommand(client, "config sync.2.path " + syncDir);
})
);
output.push(client);
}
await Promise.all(promises);
return output;
}
function randomElement(array) {
if (!array.length) return null;
return array[Math.floor(Math.random() * array.length)];
}
function randomWord() {
const words = [
"belief",
"scandalous",
"flawless",
"wrestle",
"sort",
"moldy",
"carve",
"incompetent",
"cruel",
"awful",
"fang",
"holistic",
"makeshift",
"synonymous",
"questionable",
"soft",
"drop",
"boot",
"whimsical",
"stir",
"idea",
"adhesive",
"present",
"hilarious",
"unusual",
"divergent",
"probable",
"depend",
"suck",
"belong",
"advise",
"straight",
"encouraging",
"wing",
"clam",
"serve",
"fill",
"nostalgic",
"dysfunctional",
"aggressive",
"floor",
"baby",
"grease",
"sisters",
"print",
"switch",
"control",
"victorious",
"cracker",
"dream",
"wistful",
"adaptable",
"reminiscent",
"inquisitive",
"pushy",
"unaccountable",
"receive",
"guttural",
"two",
"protect",
"skin",
"unbiased",
"plastic",
"loutish",
"zip",
"used",
"divide",
"communicate",
"dear",
"muddled",
"dinosaurs",
"grip",
"trees",
"well-off",
"calendar",
"chickens",
"irate",
"deranged",
"trip",
"stream",
"white",
"poison",
"attack",
"obtain",
"theory",
"laborer",
"omniscient",
"brake",
"maniacal",
"curvy",
"smoke",
"babies",
"punch",
"hammer",
"toothbrush",
"same",
"crown",
"jagged",
"peep",
"difficult",
"reject",
"merciful",
"useless",
"doctor",
"mix",
"wicked",
"plant",
"quickest",
"roll",
"suffer",
"curly",
"brother",
"frighten",
"cold",
"tremendous",
"move",
"knot",
"lame",
"imaginary",
"capricious",
"raspy",
"aunt",
"loving",
"wink",
"wooden",
"hop",
"free",
"drab",
"fire",
"instrument",
"border",
"frame",
"silent",
"glue",
"decorate",
"distance",
"powerful",
"pig",
"admit",
"fix",
"pour",
"flesh",
"profuse",
"skinny",
"learn",
"filthy",
"dress",
"bloody",
"produce",
"innocent",
"meaty",
"pray",
"slimy",
"sun",
"kindhearted",
"dime",
"exclusive",
"boast",
"neat",
"ruthless",
"recess",
"grieving",
"daily",
"hateful",
"ignorant",
"fence",
"spring",
"slim",
"education",
"overflow",
"plastic",
"gaping",
"chew",
"detect",
"right",
"lunch",
"gainful",
"argue",
"cloistered",
"horses",
"orange",
"shame",
"bitter",
"able",
"sail",
"magical",
"exist",
"force",
"wheel",
"best",
"suit",
"spurious",
"partner",
"request",
"dog",
"gusty",
"money",
"gaze",
"lonely",
"company",
"pale",
"tempt",
"rat",
"flame",
"wobble",
"superficial",
"stop",
"protective",
"stare",
"tongue",
"heal",
"railway",
"idiotic",
"roll",
"puffy",
"turn",
"meeting",
"new",
"frightening",
"sophisticated",
"poke",
"elderly",
"room",
"stimulating",
"increase",
"moor",
"secret",
"lean",
"occur",
"country",
"damp",
"evanescent",
"alluring",
"oafish",
"join",
"thundering",
"cars",
"awesome",
"advice",
"unruly",
"ray",
"wind",
"anxious",
"fly",
"hammer",
"adventurous",
"shop",
"cook",
"trucks",
"nonchalant",
"addition",
"base",
"abashed",
"excuse",
"giants",
"dramatic",
"piquant",
"coach",
"possess",
"poor",
"finger",
"wide-eyed",
"aquatic",
"welcome",
"instruct",
"expert",
"evasive",
"hug",
"cute",
"return",
"mice",
"damage",
"turkey",
"quiet",
"bewildered",
"tidy",
"pointless",
"outrageous",
"medical",
"foolish",
"curve",
"grandiose",
"gullible",
"hapless",
"gleaming",
"third",
"grin",
"pipe",
"egg",
"act",
"physical",
"eager",
"side",
"milk",
"tearful",
"fertile",
"average",
"glamorous",
"strange",
"yak",
"terrific",
"thin",
"near",
"snails",
"flowery",
"authority",
"fish",
"curious",
"perpetual",
"healthy",
"health",
"match",
"fade",
"chemical",
"economic",
"drawer",
"avoid",
"lying",
"minister",
"lick",
"powder",
"decay",
"desire",
"furry",
"faint",
"beam",
"sordid",
"fax",
"tail",
"bawdy",
"cherry",
"letter",
"clover",
"ladybug",
"teeth",
"behavior",
"black",
"amazing",
"pink",
"waste",
"island",
"forgetful",
"needless",
"lock",
"waves",
"boundary",
"receipt",
"handy",
"religion",
"hypnotic",
"aftermath",
"explain",
"sense",
"mundane",
"rambunctious",
"second",
"preserve",
"alarm",
"dusty",
"event",
"blow",
"weigh",
"value",
"glorious",
"jail",
"sigh",
"cemetery",
"serious",
"yummy",
"cattle",
"understood",
"limit",
"alert",
"fear",
"lucky",
"tested",
"surround",
"dolls",
"pleasant",
"disillusioned",
"discover",
"tray",
"night",
"seemly",
"liquid",
"worry",
"pen",
"bent",
"gruesome",
"war",
"teeny-tiny",
"common",
"judge",
"symptomatic",
"bed",
"trot",
"unequaled",
"flowers",
"friends",
"damaged",
"peel",
"skip",
"show",
"twist",
"worthless",
"brush",
"look",
"behave",
"imperfect",
"week",
"petite",
"direction",
"soda",
"lively",
"coal",
"coil",
"release",
"berserk",
"books",
"impossible",
"replace",
"cough",
"chunky",
"torpid",
"discreet",
"material",
"bomb",
"soothe",
"crack",
"hope",
"license",
"frightened",
"breathe",
"maddening",
"calculator",
"committee",
"paltry",
"green",
"subsequent",
"arrest",
"gigantic",
"tasty",
"metal",
"willing",
"man",
"stem",
"nonstop",
"route",
"impulse",
"government",
"comfortable",
"include",
"literate",
"multiply",
"test",
"vast",
"exercise",
"addicted",
"agreeable",
"lace",
"toes",
"young",
"water",
"end",
"wash",
"glossy",
"round",
"staking",
"sink",
"open",
"spot",
"trip",
"fierce",
"robust",
"pastoral",
"drown",
"dress",
"machine",
"calculating",
"holiday",
"crabby",
"disgusting",
"plan",
"sleet",
"sleepy",
"typical",
"borrow",
"possible",
"curtain",
"airplane",
"industry",
"nut",
"rough",
"wacky",
"rock",
"enormous",
"uninterested",
"sugar",
"rake",
"consist",
"wrist",
"basket",
"chop",
"wet",
"street",
"known",
"settle",
"bless",
"cluttered",
"wild",
"expand",
"angle",
"snake",
"yawn",
"hate",
"flood",
"rabid",
"spiteful",
"anger",
"market",
"bizarre",
"force",
"majestic",
"scissors",
"beg",
"rifle",
"foregoing",
"cactus",
"funny",
"eggnog",
"wish",
"high-pitched",
"drop",
"camp",
"scarf",
"car",
"groan",
"wonderful",
"wealthy",
"cup",
"lock",
"available",
"previous",
"jam",
"political",
"vacation",
"three",
"desk",
"fry",
"aspiring",
"productive",
"clear",
"bored",
"flashy",
"plug",
"precede",
"abhorrent",
"muddle",
"flimsy",
"paste",
"need",
"reward",
"frail",
"obnoxious",
"creature",
"whip",
"unbecoming",
"lake",
"unused",
"chin",
"tour",
"zephyr",
"experience",
"building",
"scrub",
"correct",
"hover",
"panicky",
"scorch",
"diligent",
"hulking",
"ubiquitous",
"tedious",
"aberrant",
"file",
"accidental",
"mist",
"blue-eyed",
"trite",
"nondescript",
"cows",
"wait",
"test",
"snotty",
"amuck",
"jump",
"lackadaisical",
"grey",
"tawdry",
"strong",
"land",
"kind",
"star",
"ludicrous",
"stupid",
"telling",
"use",
"bruise",
"whirl",
"cream",
"harsh",
"aboriginal",
"substantial",
"brawny",
"tease",
"pollution",
"weather",
"degree",
"dry",
"film",
"obey",
"closed",
"dependent",
"want",
"undesirable",
"stamp",
"relax",
"foot",
"obscene",
"successful",
"wriggle",
"drain",
"greasy",
"escape",
"cross",
"odd",
"boring",
"absorbed",
"houses",
"suppose",
"suit",
"moon",
"ceaseless",
"explode",
"clap",
"pop",
"courageous",
"miss",
"notebook",
"delirious",
"form",
"pretty",
"sock",
"grotesque",
"noxious",
"record",
"stop",
"saw",
"thing",
"dislike",
"cloth",
"six",
"jar",
"unnatural",
"spiffy",
"itchy",
"secretary",
"move",
"certain",
"unkempt",
"sassy",
"queue",
"shrug",
"crow",
"heavenly",
"desert",
"screw",
"vessel",
"mug",
"encourage",
"icy",
"enthusiastic",
"throat",
"whistle",
"ignore",
"miniature",
"squeak",
"scarecrow",
"fluttering",
"hang",
"icicle",
"lie",
"juicy",
"empty",
"baseball",
"various",
"promise",
"abortive",
"descriptive",
"high",
"spy",
"faded",
"talk",
"air",
"messup",
"decorous",
"sneaky",
"mark",
"sack",
"ultra",
"chivalrous",
"lethal",
"expect",
"disgusted",
"reaction",
"fireman",
"private",
"ritzy",
"manage",
"actor",
"rely",
"uppity",
"thread",
"bat",
"space",
"underwear",
"blood",
"nine",
"maid",
"shelf",
"hanging",
"shop",
"prick",
"wound",
"sloppy",
"offer",
"increase",
"clear",
"slap",
"rude",
"poised",
"wretched",
"cause",
"quince",
"tame",
"remarkable",
"abject",
"sail",
"guide",
"subdued",
"spiky",
"debonair",
"chicken",
"tired",
"hum",
"land",
"scared",
"splendid",
"guess",
"cast",
"rub",
"magnificent",
"ants",
"overwrought",
"interfere",
"gorgeous",
"office",
"trade",
"sniff",
"melted",
"bore",
"point",
"pet",
"purple",
"brake",
"flavor",
"toe",
"prickly",
"zinc",
"homely",
"modern",
"kindly",
"whisper",
"bare",
"annoyed",
"glass",
"noisy",
"null",
"thoughtless",
"skirt",
"dock",
"rings",
"mind",
"neck",
"macho",
"wave",
"history",
"play",
"road",
"profit",
"word",
"opposite",
"dreary",
"governor",
"horse",
"trust",
"elbow",
"kiss",
"crayon",
"stitch",
"excited",
"needy",
"arrange",
"easy",
"alcoholic",
"safe",
"lumpy",
"monkey",
"smile",
"capable",
"untidy",
"extra-small",
"memory",
"selective",
"reproduce",
"old-fashioned",
"overrated",
"texture",
"knit",
"downtown",
"risk",
"pot",
"sofa",
"righteous",
"wren",
"pull",
"carry",
"aboard",
"listen",
"classy",
"thank",
"shocking",
"condition",
"root",
"attempt",
"swim",
"frog",
"hurt",
"army",
"title",
"handsomely",
"town",
"guiltless",
"thaw",
"spell",
"selfish",
"disturbed",
"tramp",
"girls",
"utopian",
"noiseless",
"trail",
"bashful",
"business",
"rhetorical",
"snail",
"sign",
"plausible",
"left",
"design",
"tall",
"violent",
"wasteful",
"beautiful",
"breezy",
"tap",
"murder",
"talented",
"needle",
"creator",
"imagine",
"flippant",
"dead",
"bone",
"coherent",
"relation",
"aromatic",
"mountainous",
"face",
"ask",
"picture",
"pedal",
"colour",
"obese",
"group",
"top",
"bubble",
"pinch",
"optimal",
"school",
"bathe",
"flagrant",
"check",
"deliver",
"pass",
"tan",
"crate",
"hose",
"debt",
"faulty",
"longing",
"hollow",
"invincible",
"afford",
"lovely",
"ticket",
"changeable",
"subtract",
"fumbling",
"responsible",
"confused",
"woman",
"touch",
"watch",
"zesty",
"library",
"jail",
"wrap",
"terrify",
"brick",
"popcorn",
"cooperative",
"peck",
"pocket",
"property",
"buzz",
"tiresome",
"digestion",
"exciting",
"nation",
"juvenile",
"shade",
"copper",
"wanting",
"deer",
"waste",
"man",
"join",
"spotty",
"amused",
"mountain",
"waggish",
"bushes",
"tense",
"river",
"heartbreaking",
"help",
"mine",
"narrow",
"smash",
"scrawny",
"tame",
"rain",
"playground",
"airport",
"astonishing",
"level",
"befitting",
"animal",
"heat",
"painful",
"cellar",
"ski",
"sedate",
"knowing",
"vigorous",
"change",
"eight",
"ship",
"work",
"strip",
"robin",
"tank",
"challenge",
"vacuous",
"representative",
"regret",
"tightfisted",
"erratic",
"club",
"imported",
"therapeutic",
"rainstorm",
"luxuriant",
"relieved",
"day",
"system",
"apologise",
"male",
"prepare",
"malicious",
"naive",
"whistle",
"curl",
"hobbies",
"trousers",
"stereotyped",
"dad",
"endurable",
"grass",
"hot",
"bomb",
"morning",
"guide",
"keen",
"plot",
"accept",
"disastrous",
"macabre",
"year",
"spicy",
"absorbing",
"sticks",
"efficient",
"drain",
"warm",
"rice",
"utter",
"fact",
"marked",
"ratty",
"chalk",
"towering",
"treat",
"nest",
"annoy",
"jealous",
"stamp",
"effect",
"cautious",
"jelly",
"feigned",
"gabby",
"corn",
"volleyball",
"pan",
"psychedelic",
"fairies",
"silent",
"zonked",
"bump",
"trouble",
"mass",
"queen",
"things",
"bury",
"sister",
"quiet",
"colossal",
"puncture",
"four",
"attend",
"love",
"wiry",
"vegetable",
"destruction",
"note",
"pies",
"resolute",
"load",
"fancy",
"tacky",
"periodic",
"abandoned",
"vivacious",
"blush",
"wrathful",
"miscreant",
"call",
"striped",
"wiggly",
"supreme",
"hand",
"impolite",
"rule",
"deserted",
"concern",
"cover",
"harbor",
"waiting",
"soggy",
"psychotic",
"ancient",
"sponge",
"domineering",
"elegant",
"impartial",
"unlock",
"abrasive",
"count",
"flight",
"neighborly",
"roof",
"bulb",
"auspicious",
"automatic",
"magic",
"sign",
"amusing",
"orange",
"branch",
"sulky",
"attack",
"fetch",
"number",
"jellyfish",
"start",
"alike",
"touch",
"sour",
"wary",
"minor",
"punish",
"connect",
"protest",
"pie",
"kaput",
"doubtful",
"friendly",
"simplistic",
"smart",
"vanish",
"applaud",
"jumbled",
"ready",
"yell",
"support",
"squash",
"raise",
"parallel",
"super",
"jazzy",
"crush",
"apathetic",
"water",
"food",
"thrill",
"permit",
"heady",
"last",
"mine",
"signal",
"smoke",
"preach",
"x-ray",
"name",
"birth",
"minute",
"steel",
"bedroom",
"female",
"acrid",
"riddle",
"attractive",
"earth",
"crack",
"muscle",
"alive",
"guarded",
"sweater",
"donkey",
"doubt",
"lettuce",
"magenta",
"live",
"farm",
"glib",
"bow",
"fascinated",
"friend",
"practise",
"remember",
"bleach",
"hungry",
"voiceless",
"pin",
"sparkling",
"report",
"arm",
"sad",
"shaggy",
"parcel",
"wail",
"flash",
"territory",
"functional",
"wise",
"screeching",
"appliance",
"future",
"appear",
"team",
"rabbit",
"porter",
"paint",
"flat",
"amusement",
"ocean",
"head",
"geese",
"wash",
"embarrassed",
"tub",
"boundless",
"freezing",
"mushy",
"surprise",
"temporary",
"marble",
"recondite",
"telephone",
"zipper",
"pine",
"reign",
"pump",
"tangy",
"reply",
"toys",
"purpose",
"songs",
"form",
"delicious",
"wood",
"horn",
"nutty",
"fruit",
"lumber",
"potato",
"cheat",
"cloudy",
"visit",
"reduce",
"destroy",
"deafening",
"full",
"warlike",
"mitten",
"cover",
"earthy",
"seashore",
"yarn",
"tenuous",
"pause",
"boil",
"dogs",
"tough",
"knife",
"shy",
"naughty",
"existence",
"fire",
"eminent",
"remove",
"juice",
"sleep",
"voyage",
"balance",
"unsightly",
"plough",
"ill-fated",
"pumped",
"motionless",
"allow",
"trade",
"warm",
"toad",
"wave",
"wall",
"pigs",
"circle",
"rejoice",
"ear",
"drink",
"found",
"taboo",
"object",
"old",
"temper",
"plant",
"public",
"picayune",
"blot",
"delight",
"carpenter",
"dispensable",
"tire",
"cow",
"furniture",
"rightful",
"mute",
"gentle",
"gifted",
"ragged",
"stiff",
"retire",
"compare",
"sable",
"hole",
"judicious",
"chilly",
"sparkle",
"futuristic",
"love",
"bubble",
"travel",
"name",
"numberless",
"succeed",
"acoustic",
"lowly",
"society",
"injure",
"agree",
"reason",
"party",
"wool",
"careful",
"hook",
"bell",
"ball",
"attach",
"scream",
"development",
"happy",
"appreciate",
"disagree",
"request",
"march",
"rampant",
"scrape",
"sack",
"hair",
"measure",
"owe",
"grubby",
"vein",
"boy",
"punishment",
"smoggy",
"wry",
"immense",
"shoe",
"pack",
"brash",
"cave",
"sincere",
"adorable",
"fantastic",
"attraction",
"racial",
"jittery",
"defiant",
"honey",
"paper",
"weight",
"bee",
"blind",
"birthday",
"toothsome",
"trick",
"guard",
"fog",
"handle",
"dirty",
"salt",
"rinse",
"nippy",
"observe",
"suggestion",
"weak",
"instinctive",
"frequent",
"detail",
"verse",
"quirky",
"scattered",
"toy",
"aware",
"distribution",
"repulsive",
"draconian",
"bucket",
"harm",
"radiate",
"bang",
"shrill",
"living",
"rhythm",
"obsequious",
"drum",
"inject",
"skate",
"beds",
"smash",
"order",
"stitch",
"ground",
"nosy",
"kick",
"dusty",
"home",
"rot",
"frame",
"jam",
"sky",
"soap",
"rescue",
"energetic",
"grape",
"massive",
"deeply",
"dazzling",
"park",
"pull",
"number",
"abundant",
"barbarous",
"drag",
"ajar",
"close",
"moan",
"haircut",
"shade",
"married",
"cats",
"thirsty",
"dirt",
"vagabond",
"fearful",
"squealing",
"squalid",
"zebra",
"murky",
"sheet",
"fat",
"follow",
"bikes",
"unpack",
"materialistic",
"surprise",
"arch",
"selection",
"acoustics",
"helpless",
"thoughtful",
"cry",
"quarrelsome",
"arrogant",
"illegal",
"sudden",
"elite",
"tomatoes",
"spoil",
"flower",
"shivering",
"front",
"caption",
"volcano",
"ugliest",
"ambitious",
"pickle",
"interrupt",
"nervous",
"approve",
"messy",
"dust",
"oceanic",
"brass",
"tremble",
"fine",
"nerve",
"lunchroom",
"hard",
"engine",
"erect",
"flower",
"cynical",
"irritating",
"tight",
"cobweb",
"gray",
"invention",
"snatch",
"account",
"sharp",
"spooky",
"squeamish",
"eatable",
"share",
"need",
"moaning",
"suspect",
"rush",
"rural",
"false",
"float",
"bite",
"careless",
"sidewalk",
"cowardly",
"stroke",
"educated",
"ugly",
"type",
"wandering",
"bolt",
"mint",
"fit",
"large",
"extra-large",
"defeated",
"kitty",
"tacit",
"abiding",
"grandfather",
"trains",
"lamp",
"habitual",
"fast",
"offbeat",
"accurate",
"many",
"fortunate",
"lyrical",
"charge",
"illustrious",
"transport",
"wakeful",
"cable",
"ordinary",
"string",
"question",
"train",
"fancy",
"kick",
"enchanting",
"jobless",
"ahead",
"comparison",
"loose",
"dance",
"add",
"wonder",
"stale",
"earn",
"reflective",
"bright",
"true",
"statuesque",
"amount",
"matter",
"repair",
"care",
"ruin",
"terrible",
"elastic",
"spiders",
"craven",
"lamentable",
"decision",
"swing",
"connection",
"gaudy",
"knowledge",
"cheap",
"lazy",
"step",
"dinner",
"rod",
"agreement",
"comb",
"mean",
"past",
"knotty",
"busy",
"quicksand",
"match",
"early",
"long",
"onerous",
"ambiguous",
"worried",
"spade",
"happen",
"crook",
"dapper",
"grate",
"announce",
"plate",
"haunt",
"friction",
"actually",
"chance",
"example",
"rapid",
"zealous",
"necessary",
"ink",
"mere",
"shock",
"huge",
"jaded",
"spill",
"store",
"fuzzy",
"table",
"bottle",
"halting",
"spark",
"end",
"remain",
"transport",
"seat",
"leg",
"long-term",
"clip",
"grumpy",
"shake",
"walk",
"try",
"action",
"soup",
"short",
"hurry",
"square",
"belligerent",
"thankful",
"beginner",
"small",
"bumpy",
"silly",
"badge",
"marvelous",
"wealth",
"open",
"unequal",
"scatter",
"pest",
"fool",
"step",
"groovy",
"childlike",
"door",
"bouncy",
"believe",
"incredible",
"box",
"unhealthy",
"swanky",
"abrupt",
"depressed",
"flaky",
"famous",
"detailed",
"regret",
"envious",
"natural",
"apparel",
"spare",
"mark",
"ten",
"power",
"glistening",
"arrive",
"animated",
"slip",
"heap",
"shaky",
"unfasten",
"contain",
"inexpensive",
"introduce",
"shallow",
"rule",
"gather",
"pump",
"humorous",
"acceptable",
"womanly",
"giddy",
"silk",
"yoke",
"straw",
"invite",
"one",
"red",
"growth",
"unadvised",
"measly",
"flap",
"puzzled",
"regular",
"painstaking",
"little",
"plain",
"tumble",
"rest",
"fabulous",
"melt",
"label",
"truculent",
"internal",
"passenger",
"zippy",
"bright",
"earsplitting",
"tooth",
"veil",
"grip",
"square",
"stuff",
"gate",
"level",
"stone",
"observation",
"time",
"workable",
"bird",
"realise",
"spotted",
"coast",
"quiver",
"rebel",
"entertain",
"rotten",
"loss",
"collect",
"meal",
"satisfy",
"military",
"bake",
"cagey",
"redundant",
"treatment",
"knock",
"blink",
"scale",
"board",
"fair",
"nebulous",
"sip",
"homeless",
"place",
"complain",
"joke",
"bat",
"winter",
"choke",
"frantic",
"chubby",
"highfalutin",
"troubled",
"whole",
"rose",
"delightful",
"loaf",
"afraid",
"sturdy",
"class",
"cultured",
"yielding",
"broken",
"kittens",
"absurd",
"discovery",
"next",
"disarm",
"dangerous",
"lively",
"reflect",
"chief",
"teeny",
"pencil",
"ban",
"grade",
"size",
"dashing",
"thought",
"breath",
"empty",
"hellish",
"shock",
"sea",
"weary",
"payment",
"limping",
"premium",
"grateful",
"somber",
"tax",
"coach",
"vulgar",
"stretch",
"tow",
"branch",
"insurance",
"yam",
"stormy",
"wish",
"snow",
"cute",
"milky",
"battle",
"far",
"roasted",
"slip",
"adamant",
"awake",
"employ",
"tangible",
"tickle",
"jog",
"hysterical",
"meddle",
"parsimonious",
"judge",
"educate",
"respect",
"sound",
"oven",
"gratis",
"station",
"train",
"purring",
"steady",
"carriage",
"humdrum",
"voracious",
"jolly",
"brainy",
"proud",
"elfin",
"cure",
"motion",
"record",
"quizzical",
"pail",
"bike",
"faithful",
"approval",
"vague",
"fall",
"store",
"normal",
"rock",
"bear",
"bounce",
"giant",
"satisfying",
"crooked",
"lopsided",
"vest",
"separate",
"sneeze",
"teaching",
"general",
"meat",
"festive",
"historical",
"line",
"north",
"tip",
"son",
"damaging",
"nimble",
"broad",
"list",
"confuse",
"first",
"deserve",
"steep",
"last",
"rich",
"oval",
"thick",
"glow",
"great",
"clammy",
"cheer",
"untidy",
"scientific",
"noise",
"stomach",
"undress",
"big",
"bite-sized",
"enter",
"cake",
"aloof",
"abnormal",
"month",
"grab",
"well-groomed",
"silver",
"art",
"berry",
"giraffe",
"complete",
"billowy",
"thumb",
"squeal",
"crib",
"discussion",
"even",
"stretch",
"mellow",
"angry",
"grouchy",
"absent",
"snow",
"middle",
"stingy",
"mourn",
"deep",
"honorable",
"nice",
"verdant",
"reach",
"lavish",
"sin",
"interest",
"whine",
"tug",
"vengeful",
"rhyme",
"stay",
"upset",
"hesitant",
"tent",
"wire",
"gold",
"momentous",
"yellow",
"cap",
"delicate",
"youthful",
"twig",
"burly",
"devilish",
"chess",
"wide",
"misty",
"useful",
"memorise",
"madly",
"plants",
"spectacular",
"accessible",
"collar",
"truck",
"harmony",
"uncovered",
"beef",
"low",
"channel",
"abusive",
"analyse",
"observant",
"snobbish",
"duck",
"excellent",
"intend",
"wreck",
"testy",
"care",
"shoes",
"charming",
"demonic",
"can",
"wipe",
"acidic",
"watch",
"decisive",
"brave",
"greet",
"imminent",
"influence",
"oranges",
"seal",
"eggs",
"knowledgeable",
"ashamed",
"shiny",
"inconclusive",
"remind",
"house",
"solid",
"quixotic",
"describe",
"support",
];
return randomElement(words);
}
function execCommand(client, command, options = {}) {
let exePath = "node " + joplinAppPath;
let cmd = exePath + " --update-geolocation-disabled --env dev --log-level debug --profile " + client.profileDir + " " + command;
logger.info(client.id + ": " + command);
if (options.killAfter) {
logger.info("Kill after: " + options.killAfter);
}
return new Promise((resolve, reject) => {
let childProcess = exec(cmd, (error, stdout, stderr) => {
if (error) {
if (error.signal == "SIGTERM") {
resolve("Process was killed");
} else {
logger.error(stderr);
reject(error);
}
} else {
resolve(stdout.trim());
}
});
if (options.killAfter) {
setTimeout(() => {
logger.info("Sending kill signal...");
childProcess.kill();
}, options.killAfter);
}
});
}
async function clientItems(client) {
let itemsJson = await execCommand(client, "dump");
try {
return JSON.parse(itemsJson);
} catch (error) {
throw new Error("Cannot parse JSON: " + itemsJson);
}
}
function randomTag(items) {
let tags = [];
for (let i = 0; i < items.length; i++) {
if (items[i].type_ != 5) continue;
tags.push(items[i]);
}
return randomElement(tags);
}
function randomNote(items) {
let notes = [];
for (let i = 0; i < items.length; i++) {
if (items[i].type_ != 1) continue;
notes.push(items[i]);
}
return randomElement(notes);
}
async function execRandomCommand(client) {
let possibleCommands = [
["mkbook {word}", 40], // CREATE FOLDER
["mknote {word}", 70], // CREATE NOTE
[
async () => {
// DELETE RANDOM ITEM
let items = await clientItems(client);
let item = randomElement(items);
if (!item) return;
if (item.type_ == 1) {
return execCommand(client, "rm -f " + item.id);
} else if (item.type_ == 2) {
return execCommand(client, "rm -r -f " + item.id);
} else if (item.type_ == 5) {
// tag
} else {
throw new Error("Unknown type: " + item.type_);
}
},
30,
],
[
async () => {
// SYNC
let avgSyncDuration = averageSyncDuration();
let options = {};
if (!isNaN(avgSyncDuration)) {
if (Math.random() >= 0.5) {
options.killAfter = avgSyncDuration * Math.random();
}
}
return execCommand(client, "sync --random-failures", options);
},
30,
],
[
async () => {
// UPDATE RANDOM ITEM
let items = await clientItems(client);
let item = randomNote(items);
if (!item) return;
return execCommand(client, "set " + item.id + ' title "' + randomWord() + '"');
},
50,
],
[
async () => {
// ADD TAG
let items = await clientItems(client);
let note = randomNote(items);
if (!note) return;
let tag = randomTag(items);
let tagTitle = !tag || Math.random() >= 0.9 ? "tag-" + randomWord() : tag.title;
return execCommand(client, "tag add " + tagTitle + " " + note.id);
},
50,
],
];
let cmd = null;
while (true) {
cmd = randomElement(possibleCommands);
let r = 1 + Math.floor(Math.random() * 100);
if (r <= cmd[1]) break;
}
cmd = cmd[0];
if (typeof cmd === "function") {
return cmd();
} else {
cmd = cmd.replace("{word}", randomWord());
return execCommand(client, cmd);
}
}
function averageSyncDuration() {
return lodash.mean(syncDurations);
}
function randomNextCheckTime() {
let output = time.unixMs() + 1000 + Math.random() * 1000 * 120;
logger.info("Next sync check: " + time.unixMsToIso(output) + " (" + Math.round((output - time.unixMs()) / 1000) + " sec.)");
return output;
}
function findItem(items, itemId) {
for (let i = 0; i < items.length; i++) {
if (items[i].id == itemId) return items[i];
}
return null;
}
function compareItems(item1, item2) {
let output = [];
for (let n in item1) {
if (!item1.hasOwnProperty(n)) continue;
let p1 = item1[n];
let p2 = item2[n];
if (n == "notes_") {
p1.sort();
p2.sort();
if (JSON.stringify(p1) !== JSON.stringify(p2)) {
output.push(n);
}
} else {
if (p1 !== p2) output.push(n);
}
}
return output;
}
function findMissingItems_(items1, items2) {
let output = [];
for (let i = 0; i < items1.length; i++) {
let item1 = items1[i];
let found = false;
for (let j = 0; j < items2.length; j++) {
let item2 = items2[j];
if (item1.id == item2.id) {
found = true;
break;
}
}
if (!found) {
output.push(item1);
}
}
return output;
}
function findMissingItems(items1, items2) {
return [findMissingItems_(items1, items2), findMissingItems_(items2, items1)];
}
async function compareClientItems(clientItems) {
let itemCounts = [];
for (let i = 0; i < clientItems.length; i++) {
let items = clientItems[i];
itemCounts.push(items.length);
}
logger.info("Item count: " + itemCounts.join(", "));
let missingItems = findMissingItems(clientItems[0], clientItems[1]);
if (missingItems[0].length || missingItems[1].length) {
logger.error("Items are different");
logger.error(missingItems);
process.exit(1);
}
let differences = [];
let items = clientItems[0];
for (let i = 0; i < items.length; i++) {
let item1 = items[i];
for (let clientId = 1; clientId < clientItems.length; clientId++) {
let item2 = findItem(clientItems[clientId], item1.id);
if (!item2) {
logger.error("Item not found on client " + clientId + ":");
logger.error(item1);
process.exit(1);
}
let diff = compareItems(item1, item2);
if (diff.length) {
differences.push({
item1: JSON.stringify(item1),
item2: JSON.stringify(item2),
});
}
}
}
if (differences.length) {
logger.error("Found differences between items:");
logger.error(differences);
process.exit(1);
}
}
async function main(argv) {
await fs.remove(syncDir);
let clients = await createClients();
let activeCommandCounts = [];
let clientId = 0;
for (let i = 0; i < clients.length; i++) {
clients[i].activeCommandCount = 0;
}
function handleCommand(clientId) {
if (clients[clientId].activeCommandCount >= 1) return;
clients[clientId].activeCommandCount++;
execRandomCommand(clients[clientId])
.catch(error => {
logger.info("Client " + clientId + ":");
logger.error(error);
})
.then(r => {
if (r) {
logger.info("Client " + clientId + ":\n" + r.trim());
}
clients[clientId].activeCommandCount--;
});
}
let nextSyncCheckTime = randomNextCheckTime();
let state = "commands";
setInterval(async () => {
if (state == "waitForSyncCheck") return;
if (state == "syncCheck") {
state = "waitForSyncCheck";
let clientItems = [];
// Up to 3 sync operations must be performed by each clients in order for them
// to be perfectly in sync - in order for each items to send their changes
// and get those from the other clients, and to also get changes that are
// made as a result of a sync operation (eg. renaming a folder that conflicts
// with another one).
for (let loopCount = 0; loopCount < 3; loopCount++) {
for (let i = 0; i < clients.length; i++) {
let beforeTime = time.unixMs();
await execCommand(clients[i], "sync");
syncDurations.push(time.unixMs() - beforeTime);
if (syncDurations.length > 20) syncDurations.splice(0, 1);
if (loopCount === 2) {
let dump = await execCommand(clients[i], "dump");
clientItems[i] = JSON.parse(dump);
}
}
}
await compareClientItems(clientItems);
nextSyncCheckTime = randomNextCheckTime();
state = "commands";
return;
}
if (state == "waitForClients") {
for (let i = 0; i < clients.length; i++) {
if (clients[i].activeCommandCount > 0) return;
}
state = "syncCheck";
return;
}
if (state == "commands") {
if (nextSyncCheckTime <= time.unixMs()) {
state = "waitForClients";
return;
}
handleCommand(clientId);
clientId++;
if (clientId >= clients.length) clientId = 0;
}
}, 100);
}
main(process.argv).catch(error => {
logger.error(error);
});