/*
 * Copyright © 2012-2017 Inria.  All rights reserved.
 * See COPYING in top-level directory.
 */

#include <private/autogen/config.h> /* for HWLOC_WIN_SYS */
#include <hwloc.h>

#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <errno.h>
#include <assert.h>

#ifndef HAVE_MKSTEMP
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <private/misc.h> /* for S_IRWXU */
static inline int mkstemp(char *name)
{
  mktemp(name);
  return open(name, O_RDWR|O_CREAT, S_IRWXU);
}
#endif

/* mostly useful with valgrind, to check if backend cleanup properly */

static const char *get_backend_name(hwloc_topology_t topo)
{
  hwloc_obj_t root = hwloc_get_root_obj(topo);
  return hwloc_obj_get_info_by_name(root, "Backend");
}

static void assert_backend_name(hwloc_topology_t topo, const char *wanted)
{
  const char *found = get_backend_name(topo);
  int diff;
  assert(found);
  diff = strcmp(found, wanted);
  assert(!diff);
}

static void assert_foo_bar(hwloc_topology_t topo, int setornot)
{
  hwloc_obj_t root = hwloc_get_root_obj(topo);
  const char *found = hwloc_obj_get_info_by_name(root, "Foo");
  if (!setornot) {
    assert(!found);
  } else {
    int diff;
    assert(found);
    diff = strcmp(found, "Bar");
    assert(!diff);
  }
}

int main(void)
{
  hwloc_topology_t topology1, topology2;
  char *xmlbuf;
  int xmlbuflen;
  char xmlfile[] = "hwloc_backends.tmpxml.XXXXXX";
  char env[64];
  int xmlbufok = 0, xmlfileok = 0, xmlfilefd;
  const char *orig_backend_name;

  putenv("HWLOC_LIBXML_CLEANUP=1");

  printf("trying to export topology to XML buffer and file for later...\n");
  hwloc_topology_init(&topology1);
  hwloc_topology_load(topology1);
  orig_backend_name = get_backend_name(topology1);
  hwloc_obj_add_info(hwloc_get_root_obj(topology1), "Foo", "Bar");
  assert(hwloc_topology_is_thissystem(topology1));
  if (hwloc_topology_export_xmlbuffer(topology1, &xmlbuf, &xmlbuflen, 0) < 0)
    printf("XML buffer export failed (%s), ignoring\n", strerror(errno));
  else
    xmlbufok = 1;
  xmlfilefd = mkstemp(xmlfile);
  if (xmlfilefd < 0 || hwloc_topology_export_xml(topology1, xmlfile, 0) < 0)
    printf("XML file export failed (%s), ignoring\n", strerror(errno));
  else
    xmlfileok = 1;


  /* init+config+destroy without loading */
  printf("init...\n");
  hwloc_topology_init(&topology2);
  if (xmlfileok) {
    printf("switching to xml...\n");
    assert(!hwloc_topology_set_xml(topology2, xmlfile));
  }
  if (xmlbufok) {
    printf("switching to xmlbuffer...\n");
    assert(!hwloc_topology_set_xmlbuffer(topology2, xmlbuf, xmlbuflen));
  }
  printf("switching to synthetic...\n");
  hwloc_topology_set_synthetic(topology2, "pack:2 node:3 l1:2 pu:4");
  hwloc_topology_destroy(topology2);

  /* init+xml+load+destroy */
  if (xmlfileok) {
    printf("switching to xml and loading...\n");
    hwloc_topology_init(&topology2);
    assert(!hwloc_topology_set_xml(topology2, xmlfile));
    hwloc_topology_load(topology2);
    assert_backend_name(topology2, orig_backend_name);
    assert_foo_bar(topology2, 1);
    hwloc_topology_check(topology2);
    assert(!hwloc_topology_is_thissystem(topology2));
    hwloc_topology_destroy(topology2);
  }

  /* init+xmlbuf+load+destroy */
  if (xmlbufok) {
    printf("switching to xmlbuffer and loading...\n");
    hwloc_topology_init(&topology2);
    assert(!hwloc_topology_set_xmlbuffer(topology2, xmlbuf, xmlbuflen));
    hwloc_topology_load(topology2);
    assert_backend_name(topology2, orig_backend_name);
    assert_foo_bar(topology2, 1);
    hwloc_topology_check(topology2);
    assert(!hwloc_topology_is_thissystem(topology2));
    hwloc_topology_destroy(topology2);
  }

  /* init+synthetic+load+destroy */
  printf("switching to synthetic and loading...\n");
  hwloc_topology_init(&topology2);
  hwloc_topology_set_synthetic(topology2, "pack:2 node:3 l3i:2 pu:4");
  hwloc_topology_load(topology2);
  assert_backend_name(topology2, "Synthetic");
  assert_foo_bar(topology2, 0);
  assert(hwloc_get_nbobjs_by_type(topology2, HWLOC_OBJ_PU) == 2*3*2*4);
  hwloc_topology_check(topology2);
  assert(!hwloc_topology_is_thissystem(topology2));
  hwloc_topology_destroy(topology2);

  /* xmlenv+init+load+destroy */
  if (xmlfileok) {
    printf("switching to xml by env and loading...\n");
    snprintf(env, sizeof(env), "HWLOC_XMLFILE=%s", xmlfile);
    putenv(env);
    hwloc_topology_init(&topology2);
    hwloc_topology_load(topology2);
    assert_backend_name(topology2, orig_backend_name);
    assert_foo_bar(topology2, 1);
    hwloc_topology_check(topology2);
    assert(!hwloc_topology_is_thissystem(topology2));
    hwloc_topology_destroy(topology2);
  }

  /* syntheticenv+init+load+destroy, synthetic env overrides xml */
  printf("switching to synthetic by env and loading...\n");
  putenv("HWLOC_SYNTHETIC=node:3 pu:3");
  hwloc_topology_init(&topology2);
  hwloc_topology_load(topology2);
  assert_backend_name(topology2, "Synthetic");
  assert_foo_bar(topology2, 0);
  assert(hwloc_get_nbobjs_by_type(topology2, HWLOC_OBJ_PU) == 3*3);
  hwloc_topology_check(topology2);
  assert(!hwloc_topology_is_thissystem(topology2));
  hwloc_topology_destroy(topology2);

  /* componentsenv+init+load+destroy for testing defaults, overrides synthetic/xml/fsroot envs */
  printf("switching to default components by env and loading...\n");
  putenv("HWLOC_COMPONENTS=,"); /* don't set to empty since it means 'unset' on windows */
  hwloc_topology_init(&topology2);
  hwloc_topology_load(topology2);
  assert_backend_name(topology2, orig_backend_name);
  assert_foo_bar(topology2, 0);
  hwloc_topology_check(topology2);
  assert(hwloc_topology_is_thissystem(topology2));
  hwloc_topology_destroy(topology2);

  if (xmlbufok)
    hwloc_free_xmlbuffer(topology1, xmlbuf);
  if (xmlfilefd >= 0) {
    unlink(xmlfile);
    close(xmlfilefd);
  }
  hwloc_topology_destroy(topology1);

  return 0;
}
