text: #include "config.h"
  #include "../json_tok.c"
  #include "../param.c"
  #include <ccan/array_size/array_size.h>
  #include <common/channel_type.h>
  #include <common/setup.h>
  #include <stdio.h>
  
  char *fail_msg = NULL;
  bool failed = false;
  
  static bool check_fail(void) {
  	if (!failed)
  		return false;
  	failed = false;
  	return true;
  }
  
  struct command *cmd;
  
  struct command_result {
  };
  static struct command_result cmd_failed;
  
  struct command_result *command_fail(struct command *cmd,
  				    errcode_t code, const char *fmt, ...)
  {
  	failed = true;
  	va_list ap;
  	va_start(ap, fmt);
  	fail_msg = tal_vfmt(cmd, fmt, ap);
  	va_end(ap);
  	return &cmd_failed;
  }
  
  /* AUTOGENERATED MOCKS START */
  /* Generated stub for fromwire_channel_type */
  struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED)
  { fprintf(stderr, "fromwire_channel_type called!\n"); abort(); }
  /* Generated stub for fromwire_tlv */
  bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
  		  const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED,
  		  void *record UNNEEDED, struct tlv_field **fields UNNEEDED)
  { fprintf(stderr, "fromwire_tlv called!\n"); abort(); }
  /* Generated stub for json_to_channel_id */
  bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
  			struct channel_id *cid UNNEEDED)
  { fprintf(stderr, "json_to_channel_id called!\n"); abort(); }
  /* Generated stub for json_to_msat */
  bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
  		  struct amount_msat *msat UNNEEDED)
  { fprintf(stderr, "json_to_msat called!\n"); abort(); }
  /* Generated stub for json_to_node_id */
  bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
  			       struct node_id *id UNNEEDED)
  { fprintf(stderr, "json_to_node_id called!\n"); abort(); }
  /* Generated stub for json_to_outpoint */
  bool json_to_outpoint(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
  		      struct bitcoin_outpoint *op UNNEEDED)
  { fprintf(stderr, "json_to_outpoint called!\n"); abort(); }
  /* Generated stub for json_to_pubkey */
  bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
  		    struct pubkey *pubkey UNNEEDED)
  { fprintf(stderr, "json_to_pubkey called!\n"); abort(); }
  /* Generated stub for json_to_short_channel_id */
  bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
  			      struct short_channel_id *scid UNNEEDED)
  { fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); }
  /* Generated stub for json_to_txid */
  bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
  		  struct bitcoin_txid *txid UNNEEDED)
  { fprintf(stderr, "json_to_txid called!\n"); abort(); }
  /* Generated stub for segwit_addr_decode */
  int segwit_addr_decode(
      int* ver UNNEEDED,
      uint8_t* prog UNNEEDED,
      size_t* prog_len UNNEEDED,
      const char* hrp UNNEEDED,
      const char* addr
  )
  { fprintf(stderr, "segwit_addr_decode called!\n"); abort(); }
  /* Generated stub for tlv_fields_valid */
  bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED,
  		      size_t *err_index UNNEEDED)
  { fprintf(stderr, "tlv_fields_valid called!\n"); abort(); }
  /* Generated stub for towire_channel_type */
  void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED)
  { fprintf(stderr, "towire_channel_type called!\n"); abort(); }
  /* Generated stub for towire_tlv */
  void towire_tlv(u8 **pptr UNNEEDED,
  		const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED,
  		const void *record UNNEEDED)
  { fprintf(stderr, "towire_tlv called!\n"); abort(); }
  /* AUTOGENERATED MOCKS END */
  
  /* We do this lightningd-style: */
  enum command_mode {
  	CMD_NORMAL,
  	CMD_USAGE,
  	CMD_CHECK
  };
  
  struct command {
  	enum command_mode mode;
  	const char *usage;
  };
  
  void command_set_usage(struct command *cmd, const char *usage)
  {
  	cmd->usage = usage;
  }
  
  bool command_usage_only(const struct command *cmd)
  {
  	return cmd->mode == CMD_USAGE;
  }
  
  bool command_check_only(const struct command *cmd)
  {
  	return cmd->mode == CMD_CHECK;
  }
  
  struct json {
  	jsmntok_t *toks;
  	char *buffer;
  };
  
  static void convert_quotes(char *first)
  {
  	while (*first != '\0') {
  		if (*first == '\'')
  			*first = '"';
  		first++;
  	}
  }
  
  static struct json *json_parse(const tal_t * ctx, const char *str)
  {
  	struct json *j = tal(ctx, struct json);
  	j->buffer = tal_strdup(j, str);
  	convert_quotes(j->buffer);
  
  	j->toks = tal_arr(j, jsmntok_t, 50);
  	assert(j->toks);
  	jsmn_parser parser;
  
        again:
  	jsmn_init(&parser);
  	int ret = jsmn_parse(&parser, j->buffer, strlen(j->buffer), j->toks,
  			     tal_count(j->toks));
  	if (ret == JSMN_ERROR_NOMEM) {
  		tal_resize(&j->toks, tal_count(j->toks) * 2);
  		goto again;
  	}
  
  	if (ret <= 0) {
  		assert(0);
  	}
  	return j;
  }
  
  static void zero_params(void)
  {
  	struct json *j = json_parse(cmd, "{}");
  	assert(param(cmd, j->buffer, j->toks, NULL));
  
  	j = json_parse(cmd, "[]");
  	assert(param(cmd, j->buffer, j->toks, NULL));
  }
  
  struct sanity {
  	char *str;
  	bool failed;
  	int ival;
  	u64 fpval; /* floating-point, multiplied by 1000000 */
  	char *fail_str;
  };
  
  struct sanity buffers[] = {
  	// pass
  	{"['42', '3.15']", false, 42, 3150000, NULL},
  	{"{ 'u64' : '42', 'fp' : '3.15' }", false, 42, 3150000, NULL},
  
  	// fail
  	{"{'u64':'42', 'fp':'3.15', 'extra':'stuff'}", true, 0, 0,
  	 "unknown parameter"},
  	{"['42', '3.15', 'stuff']", true, 0, 0, "too many"},
  	{"['42', '3.15', 'null']", true, 0, 0, "too many"},
  
  	// not enough
  	{"{'u64':'42'}", true, 0, 0, "missing required"},
  	{"['42']", true, 0, 0, "missing required"},
  
  	// fail wrong type
  	{"{'u64':'hello', 'fp':'3.15'}", true, 0, 0, "be an unsigned 64"},
  	{"['3.15', '3.15', 'stuff']", true, 0, 0, "integer"},
  };
  
  static void stest(const struct json *j, struct sanity *b)
  {
  	u64 *ival;
  	u64 *fpval;
  	if (!param(cmd, j->buffer, j->toks,
  		   p_req("u64", param_u64, &ival),
  		   p_req("fp", param_millionths, &fpval), NULL)) {
  		assert(check_fail());
  		assert(b->failed == true);
  		if (!strstr(fail_msg, b->fail_str)) {
  			printf("%s != %s\n", fail_msg, b->fail_str);
  			assert(false);
  		}
  	} else {
  		assert(!check_fail());
  		assert(b->failed == false);
  		assert(*ival == 42);
  		assert(*fpval > 3149900 && b->fpval < 3150100);
  	}
  }
  
  static void sanity(void)
  {
  	for (int i = 0; i < ARRAY_SIZE(buffers); ++i) {
  		struct json *j = json_parse(cmd, buffers[i].str);
  		assert(j->toks->type == JSMN_OBJECT
  		       || j->toks->type == JSMN_ARRAY);
  		stest(j, &buffers[i]);
  	}
  }
  
  /*
   * Make sure toks are passed through correctly, and also make sure
   * optional missing toks are set to NULL.
   */
  static void tok_tok(void)
  {
  	{
  		unsigned int n;
  		const jsmntok_t *tok = NULL;
  		struct json *j = json_parse(cmd, "{ 'satoshi': '546' }");
  
  		assert(param(cmd, j->buffer, j->toks,
  			     p_req("satoshi", param_tok, &tok), NULL));
  		assert(tok);
  		assert(json_to_number(j->buffer, tok, &n));
  		assert(n == 546);
  	}
  	// again with missing optional parameter
  	{
  		/* make sure it is *not* NULL */
  		const jsmntok_t *tok = (const jsmntok_t *) 65535;
  
  		struct json *j = json_parse(cmd, "{}");
  		assert(param(cmd, j->buffer, j->toks,
  			     p_opt("satoshi", param_tok, &tok), NULL));
  
  		/* make sure it *is* NULL */
  		assert(tok == NULL);
  	}
  }
  
  /* check for valid but duplicate json name-value pairs */
  static void dup_names(void)
  {
  	struct json *j =
  		json_parse(cmd,
  			   "{ 'u64' : '42', 'u64' : '43', 'fp' : '3.15' }");
  
  	u64 *i;
  	u64 *fp;
  	assert(!param(cmd, j->buffer, j->toks,
  		      p_req("u64", param_u64, &i),
  		      p_req("fp", param_millionths, &fp), NULL));
  }
  
  static void null_params(void)
  {
  	uint64_t **intptrs = tal_arr(cmd, uint64_t *, 7);
  	/* no null params */
  	struct json *j =
  	    json_parse(cmd, "[ '10', '11', '12', '13', '14', '15', '16']");
  
  	assert(param(cmd, j->buffer, j->toks,
  		     p_req("0", param_u64, &intptrs[0]),
  		     p_req("1", param_u64, &intptrs[1]),
  		     p_req("2", param_u64, &intptrs[2]),
  		     p_req("3", param_u64, &intptrs[3]),
  		     p_opt_def("4", param_u64, &intptrs[4], 999),
  		     p_opt("5", param_u64, &intptrs[5]),
  		     p_opt("6", param_u64, &intptrs[6]),
  		     NULL));
  	for (int i = 0; i < tal_count(intptrs); ++i) {
  		assert(intptrs[i]);
  		assert(*intptrs[i] == i + 10);
  	}
  
  	/* missing at end */
  	j = json_parse(cmd, "[ '10', '11', '12', '13', '14']");
  	assert(param(cmd, j->buffer, j->toks,
  		     p_req("0", param_u64, &intptrs[0]),
  		     p_req("1", param_u64, &intptrs[1]),
  		     p_req("2", param_u64, &intptrs[2]),
  		     p_req("3", param_u64, &intptrs[3]),
  		     p_opt("4", param_u64, &intptrs[4]),
  		     p_opt("5", param_u64, &intptrs[5]),
  		     p_opt_def("6", param_u64, &intptrs[6], 888),
  		     NULL));
  	assert(*intptrs[0] == 10);
  	assert(*intptrs[1] == 11);
  	assert(*intptrs[2] == 12);
  	assert(*intptrs[3] == 13);
  	assert(*intptrs[4] == 14);
  	assert(!intptrs[5]);
  	assert(*intptrs[6] == 888);
  }
  
  static void no_params(void)
  {
  	struct json *j = json_parse(cmd, "[]");
  	assert(param(cmd, j->buffer, j->toks, NULL));
  
  	j = json_parse(cmd, "[ 'unexpected' ]");
  	assert(!param(cmd, j->buffer, j->toks, NULL));
  }
  
  
  #if DEVELOPER
  /*
   * Check to make sure there are no programming mistakes.
   */
  static void bad_programmer(void)
  {
  	u64 *ival;
  	u64 *ival2;
  	u64 *fpval;
  	struct json *j = json_parse(cmd, "[ '25', '546', '26' ]");
  
  	/* check for repeated names */
  	assert(!param(cmd, j->buffer, j->toks,
  		      p_req("repeat", param_u64, &ival),
  		      p_req("fp", param_millionths, &fpval),
  		      p_req("repeat", param_u64, &ival2), NULL));
  	assert(check_fail());
  	assert(strstr(fail_msg, "developer error"));
  
  	assert(!param(cmd, j->buffer, j->toks,
  		      p_req("repeat", param_u64, &ival),
  		      p_req("fp", param_millionths, &fpval),
  		      p_req("repeat", param_u64, &ival), NULL));
  	assert(check_fail());
  	assert(strstr(fail_msg, "developer error"));
  
  	assert(!param(cmd, j->buffer, j->toks,
  		      p_req("u64", param_u64, &ival),
  		      p_req("repeat", param_millionths, &fpval),
  		      p_req("repeat", param_millionths, &fpval), NULL));
  	assert(check_fail());
  	assert(strstr(fail_msg, "developer error"));
  
  	/* check for repeated arguments */
  	assert(!param(cmd, j->buffer, j->toks,
  		      p_req("u64", param_u64, &ival),
  		      p_req("repeated-arg", param_u64, &ival), NULL));
  	assert(check_fail());
  	assert(strstr(fail_msg, "developer error"));
  
  	assert(!param(cmd, j->buffer, j->toks,
  		      p_req("u64", (param_cbx) NULL, NULL), NULL));
  	assert(check_fail());
  	assert(strstr(fail_msg, "developer error"));
  
  	/* Add required param after optional */
  	j = json_parse(cmd, "[ '25', '546', '26', '1.1' ]");
  	unsigned int *msatoshi;
  	u64 *riskfactor_millionths;
  	assert(!param(
  	    cmd, j->buffer, j->toks, p_req("u64", param_u64, &ival),
  	    p_req("fp", param_millionths, &fpval),
  	    p_opt_def("msatoshi", param_number, &msatoshi, 100),
  	    p_req("riskfactor", param_millionths, &riskfactor_millionths),
  	    NULL));
  	assert(*msatoshi);
  	assert(*msatoshi == 100);
  	assert(check_fail());
  	assert(strstr(fail_msg, "developer error"));
  }
  #endif
  
  static void add_members(struct param **params,
  			char **obj,
  			char **arr, unsigned int **ints)
  {
  	for (int i = 0; i < tal_count(ints); ++i) {
  		const char *name = tal_fmt(*params, "%i", i);
  		if (i != 0) {
  			tal_append_fmt(obj, ", ");
  			tal_append_fmt(arr, ", ");
  		}
  		tal_append_fmt(obj, "\"%i\" : %i", i, i);
  		tal_append_fmt(arr, "%i", i);
  		param_add(params, name, true,
  			  typesafe_cb_preargs(struct command_result *, void **,
  					      param_number,
  					      &ints[i],
  					      struct command *,
  					      const char *,
  					      const char *,
  					      const jsmntok_t *),
  			  &ints[i]);
  	}
  }
  
  /*
   * A roundabout way of initializing an array of ints to:
   * ints[0] = 0, ints[1] = 1, ... ints[499] = 499
   */
  static void five_hundred_params(void)
  {
  	struct param *params = tal_arr(NULL, struct param, 0);
  
  	unsigned int **ints = tal_arr(params, unsigned int*, 500);
  	char *obj = tal_fmt(params, "{ ");
  	char *arr = tal_fmt(params, "[ ");
  	add_members(&params, &obj, &arr, ints);
  	tal_append_fmt(&obj, "}");
  	tal_append_fmt(&arr, "]");
  
  	/* first test object version */
  	struct json *j = json_parse(params, obj);
  	assert(param_arr(cmd, j->buffer, j->toks, params, false) == NULL);
  	for (int i = 0; i < tal_count(ints); ++i) {
  		assert(ints[i]);
  		assert(*ints[i] == i);
  		*ints[i] = 65535;
  	}
  
  	/* now test array */
  	j = json_parse(params, arr);
  	assert(param_arr(cmd, j->buffer, j->toks, params, false) == NULL);
  	for (int i = 0; i < tal_count(ints); ++i) {
  		assert(*ints[i] == i);
  	}
  
  	tal_free(params);
  }
  
  static void sendpay(void)
  {
  	struct json *j = json_parse(cmd, "[ 'A', '123', 'hello there' '547']");
  
  	const jsmntok_t *routetok, *note;
  	u64 *msatoshi;
  	unsigned *cltv;
  
  	if (!param(cmd, j->buffer, j->toks,
  		   p_req("route", param_tok, &routetok),
  		   p_req("cltv", param_number, &cltv),
  		   p_opt("note", param_tok, &note),
  		   p_opt("msatoshi", param_u64, &msatoshi),
  		   NULL))
  		assert(false);
  
  	assert(note);
  	assert(!strncmp("hello there", j->buffer + note->start,
  			note->end - note->start));
  	assert(msatoshi);
  	assert(*msatoshi == 547);
  }
  
  static void sendpay_nulltok(void)
  {
  	struct json *j = json_parse(cmd, "[ 'A', '123']");
  
  	const jsmntok_t *routetok, *note = (void *) 65535;
  	u64 *msatoshi;
  	unsigned *cltv;
  
  	if (!param(cmd, j->buffer, j->toks,
  		   p_req("route", param_tok, &routetok),
  		   p_req("cltv", param_number, &cltv),
  		   p_opt("note", param_tok, &note),
  		   p_opt("msatoshi", param_u64, &msatoshi),
  		   NULL))
  		assert(false);
  
  	assert(note == NULL);
  	assert(msatoshi == NULL);
  }
  
  static void advanced(void)
  {
  	{
  		struct json *j = json_parse(cmd, "[ 'lightning', 24, 'tok', 543 ]");
  
  		struct json_escape *label;
  		u64 *msat;
  		u64 *msat_opt1, *msat_opt2;
  		const jsmntok_t *tok;
  
  		assert(param(cmd, j->buffer, j->toks,
  			     p_req("description", param_label, &label),
  			     p_req("msat", param_u64, &msat),
  			     p_req("tok", param_tok, &tok),
  			     p_opt("msat_opt1", param_u64, &msat_opt1),
  			     p_opt("msat_opt2", param_u64, &msat_opt2),
  			     NULL));
  		assert(label != NULL);
  		assert(streq(label->s, "lightning"));
  		assert(*msat == 24);
  		assert(tok);
  		assert(msat_opt1);
  		assert(*msat_opt1 == 543);
  		assert(msat_opt2 == NULL);
  	}
  	{
  		struct json *j = json_parse(cmd, "[ 3, 'foo' ]");
  		struct json_escape *label, *foo;
  		assert(param(cmd, j->buffer, j->toks,
  			      p_req("label", param_label, &label),
  			      p_opt("foo", param_label, &foo),
  			      NULL));
  		assert(streq(label->s, "3"));
  		assert(streq(foo->s, "foo"));
  	}
  	{
  		u64 *msat;
  		u64 *msat2;
  		struct json *j = json_parse(cmd, "[ 3 ]");
  		assert(param(cmd, j->buffer, j->toks,
  			      p_opt_def("msat", param_u64, &msat, 23),
  			      p_opt_def("msat2", param_u64, &msat2, 53),
  			      NULL));
  		assert(*msat == 3);
  		assert(msat2);
  		assert(*msat2 == 53);
  	}
  }
  
  static void advanced_fail(void)
  {
  	{
  		struct json *j = json_parse(cmd, "[ 'anyx' ]");
  		u64 *msat;
  		assert(!param(cmd, j->buffer, j->toks,
  			      p_req("msat", param_u64, &msat),
  			      NULL));
  		assert(check_fail());
  		assert(strstr(fail_msg, "msat: should be an unsigned 64 bit integer: invalid token '\"anyx\"'"));
  	}
  }
  
  #define test_cb(cb, T, json_, value, pass) \
  { \
  	struct json *j = json_parse(cmd, json_); \
  	T *v; \
  	struct command_result *ret = cb(cmd, "name", j->buffer, j->toks + 1, &v); \
  	assert((ret == NULL) == pass);					\
  	if (ret == NULL) { \
  		assert(v); \
  		assert(*v == value); \
  	} \
  }
  
  static void param_tests(void)
  {
  	test_cb(param_bool, bool, "[ true ]", true, true);
  	test_cb(param_bool, bool, "[ false ]", false, true);
  	test_cb(param_bool, bool, "[ tru ]", false, false);
  	test_cb(param_bool, bool, "[ 1 ]", false, false);
  
  	test_cb(param_millionths, u64, "[ -0.01 ]", 0, false);
  	test_cb(param_millionths, u64, "[ 0.00 ]", 0, true);
  	test_cb(param_millionths, u64, "[ 1 ]", 1000000, true);
  	test_cb(param_millionths, u64, "[ 1.1 ]", 1100000, true);
  	test_cb(param_millionths, u64, "[ 1.01 ]", 1010000, true);
  	test_cb(param_millionths, u64, "[ 99.99 ]", 99990000, true);
  	test_cb(param_millionths, u64, "[ 100.0 ]", 100000000, true);
  	test_cb(param_millionths, u64, "[ 100.001 ]", 100001000, true);
  	test_cb(param_millionths, u64, "[ 1000 ]", 1000000000, true);
  	test_cb(param_millionths, u64, "[ 'wow' ]", 0, false);
  }
  
  static void test_invoice(struct command *cmd,
  			 const char *buffer,
  			 const jsmntok_t *obj UNNEEDED,
  			 const jsmntok_t *params)
  {
  	u64 *msatoshi_val;
  	struct json_escape *label_val;
  	const char *desc_val;
  	u64 *expiry;
  	const jsmntok_t *fallbacks;
  	const jsmntok_t *preimagetok;
  
  	assert(cmd->mode == CMD_USAGE);
  	if (!param(cmd, buffer, params,
  		  p_req("msatoshi", param_u64, &msatoshi_val),
  		  p_req("label", param_label, &label_val),
  		  p_req("description", param_escaped_string, &desc_val),
  		  p_opt("expiry", param_u64, &expiry),
  		  p_opt("fallbacks", param_array, &fallbacks),
  		  p_opt("preimage", param_tok, &preimagetok), NULL))
  		return;
  
  	/* should not be here since we are in the mode of CMD_USAGE
  	 * and it always returns false. */
  	abort();
  }
  
  static void usage(void)
  {
  	cmd->mode = CMD_USAGE;
  
  	test_invoice(cmd, NULL, NULL, NULL);
  	assert(streq(cmd->usage,
  		     "msatoshi label description "
  		     "[expiry] [fallbacks] [preimage]"));
  
  	cmd->mode = CMD_NORMAL;
  }
  
  int main(int argc, char *argv[])
  {
  	common_setup(argv[0]);
  	cmd = tal(tmpctx, struct command);
  	cmd->mode = CMD_NORMAL;
  	fail_msg = tal_arr(cmd, char, 10000);
  
  	zero_params();
  	sanity();
  	tok_tok();
  	null_params();
  	no_params();
  #if DEVELOPER
  	bad_programmer();
  #endif
  	dup_names();
  	five_hundred_params();
  	sendpay();
  	sendpay_nulltok();
  	advanced();
  	advanced_fail();
  	param_tests();
  	usage();
  
  	printf("run-params ok\n");
  	common_shutdown();
  }
  