text: '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`;
  const 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() {
  	const output = [];
  	const promises = [];
  	for (let clientId = 0; clientId < 2; clientId++) {
  		const 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 = {}) {
  	const exePath = `node ${joplinAppPath}`;
  	const 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) => {
  		const 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) {
  	const itemsJson = await execCommand(client, 'dump');
  	try {
  		return JSON.parse(itemsJson);
  	} catch (error) {
  		throw new Error(`Cannot parse JSON: ${itemsJson}`);
  	}
  }
  
  function randomTag(items) {
  	const 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) {
  	const 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) {
  	const possibleCommands = [
  		['mkbook {word}', 40], // CREATE FOLDER
  		['mknote {word}', 70], // CREATE NOTE
  		[
  			async () => {
  				// DELETE RANDOM ITEM
  				const items = await clientItems(client);
  				const 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
  				const avgSyncDuration = averageSyncDuration();
  				const 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
  				const items = await clientItems(client);
  				const item = randomNote(items);
  				if (!item) return;
  
  				return execCommand(client, `set ${item.id} title "${randomWord()}"`);
  			},
  			50,
  		],
  		[
  			async () => {
  				// ADD TAG
  				const items = await clientItems(client);
  				const note = randomNote(items);
  				if (!note) return;
  
  				const tag = randomTag(items);
  				const 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);
  		const 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() {
  	const 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) {
  	const output = [];
  	for (const n in item1) {
  		if (!item1.hasOwnProperty(n)) continue;
  		const p1 = item1[n];
  		const 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) {
  	const output = [];
  
  	for (let i = 0; i < items1.length; i++) {
  		const item1 = items1[i];
  		let found = false;
  		for (let j = 0; j < items2.length; j++) {
  			const 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) {
  	const itemCounts = [];
  	for (let i = 0; i < clientItems.length; i++) {
  		const items = clientItems[i];
  		itemCounts.push(items.length);
  	}
  	logger.info(`Item count: ${itemCounts.join(', ')}`);
  
  	const missingItems = findMissingItems(clientItems[0], clientItems[1]);
  	if (missingItems[0].length || missingItems[1].length) {
  		logger.error('Items are different');
  		logger.error(missingItems);
  		process.exit(1);
  	}
  
  	const differences = [];
  	const items = clientItems[0];
  	for (let i = 0; i < items.length; i++) {
  		const item1 = items[i];
  		for (let clientId = 1; clientId < clientItems.length; clientId++) {
  			const item2 = findItem(clientItems[clientId], item1.id);
  			if (!item2) {
  				logger.error(`Item not found on client ${clientId}:`);
  				logger.error(item1);
  				process.exit(1);
  			}
  
  			const 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() {
  	await fs.remove(syncDir);
  
  	const clients = await createClients();
  	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';
  			const 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++) {
  					const beforeTime = time.unixMs();
  					await execCommand(clients[i], 'sync');
  					syncDurations.push(time.unixMs() - beforeTime);
  					if (syncDurations.length > 20) syncDurations.splice(0, 1);
  					if (loopCount === 2) {
  						const dump = await execCommand(clients[i], 'dump');
  						clientItems[i] = JSON.parse(dump);
  					}
  				}
  			}
  
  			await compareClientItems(clientItems);
  
  			nextSyncCheckTime = randomNextCheckTime();
  			// eslint-disable-next-line require-atomic-updates
  			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);
  });
  