/*
 * Copyright IBM Corporation. 2008
 *
 * Author:	Sudhir Kumar <skumar@linux.vnet.ibm.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2.1 of the GNU Lesser General Public License
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Description: This file contains the functions for testing libcgroup apis.
 */

#include "libcgrouptest.h"

/* The messages that may be useful to the user */
char info[][SIZE] = {
	" Parameter nullcgroup\n", 			/* NULLGRP */
	" Parameter commoncgroup\n",			/* COMMONGRP */
	" Parameter not created group\n",		/* NOTCRTDGRP */
	" Parameter same cgroup\n",			/* SAMEGRP */
	" Task found in group/s\n",			/* TASKINGRP */
	" Task not found in group/s\n",			/* TASKNOTINGRP */
	" Task not found in all groups\n",		/* TASKNOTINANYGRP */
	" group found in filesystem\n",			/* GRPINFS */
	" group not found in filesystem\n",		/* GRPNOTINFS */
	" group found under both controllers\n",	/* GRPINBOTHCTLS */
	" group not found under second controller\n",	/* GRPNOTIN2NDCTL */
	" group not found under first controller\n",	/* GRPNOTIN1STCTL */
	" group modified under both controllers\n",	/* GRPMODINBOTHCTLS */
	" group not modified under second controller\n",/* GRPNOTMODIN2NDCTL */
	" group not modified under any controller\n",	/* GRPNOTMODINANYCTL */
	" Group deleted from filesystem\n",		/* GRPDELETEDINFS */
	" Group not deleted from filesystem\n",		/* GRPNOTDELETEDINFS */
	" Group not deleted globally\n",		/* GRPNOTDELETEDGLOBALY */
	/* In case there is no extra info messages to be printed */
	"\n",						/* NOMESSAGE */
};

/**
 * Tests the cgroup_init_cgroup() api under different scenarios
 * @param retcode error code in case any error is expected from api
 * @param i the test number
 */
void test_cgroup_init(int retcode, int i)
{
	int retval;

	retval = cgroup_init();
	if (retval == retcode)
		message(i, PASS, "init()\t", retval, info[NOMESSAGE]);
	else
		message(i, FAIL, "init()",  retval, info[NOMESSAGE]);
}

/**
 * Tests the cgroup_attach_cgroup() api under different scenarios
 * @param retcode error code in case any error is expected from api
 * @param cgrp the group to assign the task to
 * @param group1 the name of the group under first (single) mountpoint
 * @param group2 the name of the group under 2nd moutpoint for multimount
 * @param i the test number
 * @param k the message enum number to print the useful message
 */
void test_cgroup_attach_task(int retcode, struct cgroup *cgrp,
	 const char *group1, const char *group2, pid_t pid, int k, int i)
{
	int retval;
	char tasksfile[FILENAME_MAX], tasksfile2[FILENAME_MAX];
	/* Check, In case some error is expected due to a negative scenario */
	if (retcode) {
		if (pid)
			retval = cgroup_attach_task_pid(cgrp, pid);
		else
			retval = cgroup_attach_task(cgrp);

		if (retval == retcode)
			message(i, PASS, "attach_task()", retval, info[k]);
		else
			message(i, FAIL, "attach_task()", retval, info[k]);

		return;
	}

	/* Now there is no error and it is a genuine call */
	if (pid)
		retval = cgroup_attach_task_pid(cgrp, pid);
	else
		retval = cgroup_attach_task(cgrp);

	/* API returned success, so perform check */
	if (retval == 0) {
		build_path(tasksfile, mountpoint,
					 group1, "tasks");

		if (check_task(tasksfile, 0)) {
			if (fs_mounted == 2) {
				/* multiple mounts */
				build_path(tasksfile2, mountpoint2,
							 group2, "tasks");
				if (check_task(tasksfile2, 0)) {
					message(i, PASS, "attach_task()",
						 retval, info[TASKINGRP]);
				} else {
					message(i, FAIL, "attach_task()",
						 retval, info[TASKNOTINANYGRP]);
				}
			} else {
				/* single mount */
				message(i, PASS, "attach_task()",
						 retval, info[TASKINGRP]);
			}
		} else {
			message(i, FAIL, "attach_task()", retval,
							 info[TASKNOTINGRP]);
		}
	} else {
		message(i, FAIL, "attach_task()", retval, (char *)"\n");
	}
}

/**
 * This function creates a cgroup data structure
 * This function is a bit ugly for now and need to be changed
 * @param ctl the controller under which group is to be created
 * @param grpname the name of the group
 * @param value_type which value out of four types
 * @param struct cval the control value structure
 * @param struct ids the permissions struct
 * @param the test number
 */
struct cgroup *create_new_cgroup_ds(int ctl, const char *grpname,
	 int value_type, struct cntl_val_t cval, struct uid_gid_t ids, int i)
{
	int retval;
	char group[FILENAME_MAX];
	char controller_name[FILENAME_MAX], control_file[FILENAME_MAX];

	strncpy(group, grpname, sizeof(group));
	retval = set_controller(ctl, controller_name, control_file);
	if (retval) {
		fprintf(stderr, "Setting controller failled\n");
		return NULL;
	}

	switch (ctl) {
		/* control values are controller specific, so will be set
		 * accordingly from the config file */
	case CPU:
		strncpy(cval.val_string, "260000", sizeof(cval.val_string));
		break;

	case MEMORY:
		strncpy(cval.val_string, "7000064", sizeof(cval.val_string));
		break;

	/* To be added for other controllers */
	default:
		printf("Invalid controller name passed. Setting control value"
					" failed. Dependent tests may fail\n");
		return NULL;
		break;
	}

	return new_cgroup(group, controller_name, control_file,
						 value_type, cval, ids, i);
}

/**
 * Tests the cgroup_create_cgroup() api under different scenarios
 * @param retcode error code in case any error is expected from api
 * @param cgrp the group to be created
 * @param name the name of the group
 * @param common to test if group will be created under one or both mountpoints
 * @param mpnt to test if group under mountpoint or mountpoint2
 * @param ign parameter for api if to ignore the ownership
 * @param the test number
 */
void test_cgroup_create_cgroup(int retcode, struct cgroup *cgrp,
			 const char *name, int common, int mpnt, int ign, int i)
{
	int retval;
	char path1_group[FILENAME_MAX], path2_group[FILENAME_MAX];
	/* Check, In case some error is expected due to a negative scenario */
	if (retcode) {
		retval = cgroup_create_cgroup(cgrp, ign);
		if (retval == retcode)
			message(i, PASS, "create_cgroup()", retval,
							 info[NOMESSAGE]);
		else
			message(i, FAIL, "create_cgroup()", retval,
							 info[NOMESSAGE]);

		return;
	}

	/* Now there is no error and it is a genuine call */
	retval = cgroup_create_cgroup(cgrp, ign);
	if (retval) {
		message(i, FAIL, "create_cgroup()", retval,  info[NOMESSAGE]);
		return;
	}

	/* Let us now check if the group exist in file system */
	if (!common) {
		/* group only under one mountpoint */
		if (mpnt == 1)
			/* group under mountpoint */
			build_path(path1_group, mountpoint, name, NULL);
		else
			/* group under mountpoint2 */
			build_path(path1_group, mountpoint2, name, NULL);

		if (group_exist(path1_group) == 0)
			message(i, PASS, "create_cgroup()", retval,
							 info[GRPINFS]);
		else
			message(i, FAIL, "create_cgroup()", retval,
							 info[GRPNOTINFS]);

	 /* group under both mountpoints */
	} else {
		/* check if the group exists under both controllers */
		build_path(path1_group, mountpoint, name, NULL);
		if (group_exist(path1_group) == 0) {
			build_path(path2_group, mountpoint2, name, NULL);

			if (group_exist(path2_group) == 0)
				message(i, PASS, "create_cgroup()",
						 retval, info[GRPINBOTHCTLS]);
			else
				message(i, FAIL, "create_cgroup()",
						 retval, info[GRPNOTIN2NDCTL]);
		} else {
			message(i, FAIL, "create_cgroup()", retval,
							 info[GRPNOTIN1STCTL]);
		}
	}

	return;
}

/**
 * Tests the cgroup_delete_cgroup() api under different scenarios
 * @param retcode error code in case any error is expected from api
 * @param cgrp the group to be deleted
 * @param name the name of the group
 * @param common to test if group was created under one or both mountpoints
 * @param mpnt to test if group under mountpoint or mountpoint2
 * @param ign parameter for api if to ignore the ownership
 * @param the test number
 */
void test_cgroup_delete_cgroup(int retcode, struct cgroup *cgrp,
			 const char *name, int common, int mpnt, int ign, int i)
{
	int retval;
	char path1_group[FILENAME_MAX], path2_group[FILENAME_MAX];
	/* Check, In case some error is expected due to a negative scenario */
	if (retcode) {
		retval = cgroup_delete_cgroup(cgrp, ign);
		if (retval == retcode)
			message(i, PASS, "delete_cgroup()", retval,
							 info[NOMESSAGE]);
		else
			message(i, FAIL, "delete_cgroup()", retval,
							 info[NOMESSAGE]);

		return;
	}

	/* Now there is no error and it is a genuine call */
	retval = cgroup_delete_cgroup(cgrp, ign);
	if (retval) {
		message(i, FAIL, "delete_cgroup()", retval,  info[NOMESSAGE]);
		return;
	}

	/* Let us now check if the group has been deleted from file system */
	if (!common) {
		/* check only under one mountpoint */
		if (mpnt == 1)
			/* check group under mountpoint */
			build_path(path1_group, mountpoint, name, NULL);
		else
			/* check group under mountpoint2 */
			build_path(path1_group, mountpoint2, name, NULL);

		if (group_exist(path1_group) == ENOENT)
			message(i, PASS, "delete_cgroup()", retval,
						 info[GRPDELETEDINFS]);
		else
			message(i, FAIL, "delete_cgroup()", retval,
						 info[GRPNOTDELETEDINFS]);

	} else {
		/* check group under both mountpoints */
		/* Check if the group deleted under both mountpoints */
		build_path(path1_group, mountpoint, name, NULL);
		if (group_exist(path1_group) == ENOENT) {
			build_path(path2_group, mountpoint2, name, NULL);

			if (group_exist(path2_group) == ENOENT)
				message(i, PASS, "delete_cgroup()",
						 retval, info[GRPDELETEDINFS]);
			else
				message(i, FAIL, "delete_cgroup()",
					 retval, info[GRPNOTDELETEDGLOBALY]);
		} else {
			message(i, FAIL, "delete_cgroup()", retval,
						 info[GRPNOTDELETEDINFS]);
		}
	}

}

/**
 * The function tests if the given controller is enabled in kernel
 * @param name the name of the controller to be checked
 * @param exist set to 1 if the controller exists
 */
void is_subsystem_enabled(const char *name, int *exist)
{
	int hierarchy, num_cgroups, enabled;
	FILE *fd;
	char subsys_name[FILENAME_MAX];

	fd = fopen("/proc/cgroups", "r");
	if (!fd)
		return;

	while (!feof(fd)) {
		fscanf(fd, "%s, %d, %d, %d", subsys_name,
					 &hierarchy, &num_cgroups, &enabled);
		if (strncmp(name, subsys_name, sizeof(*name)) == 0)
			*exist = 1;
	}

	fclose(fd);
}

/**
 * This function tests if the given group exists in filesystem
 * @param path_group path to the group to be tested for existence
 */
int group_exist(char *path_group)
{
	struct stat statbuf;
	if (stat(path_group, &statbuf) == -1) {
		/* Group deleted. OK */
		if (errno == ENOENT)
			return ENOENT;
		/* There is some other failure */
		printf("stat failed, return code is %d\n", errno);
		return -1;
	}

	if (S_ISDIR(statbuf.st_mode))
		return 0;
	else
		return -1;
}

/**
 * Sets the controller name and control file name
 * @param controller the enum for the name of the controller
 * @param controller_name name of the controller
 * @param control_file corresponding control file
 */
int set_controller(int controller, char *controller_name, char *control_file)
{
	switch (controller) {
	case MEMORY:
		if (memory == 0)
			return 1;

		strncpy(controller_name, "memory", FILENAME_MAX);
		strncpy(control_file, "memory.limit_in_bytes", FILENAME_MAX);
		return 0;
		break;

	case CPU:
		if (cpu == 0)
			return 1;

		strncpy(controller_name, "cpu", FILENAME_MAX);
		strncpy(control_file, "cpu.shares", FILENAME_MAX);
		return 0;
		break;

	case CPUSET:
		strncpy(controller_name, "cpuset", FILENAME_MAX);
		strncpy(control_file, "cpuset.cpus", FILENAME_MAX);
		return 0;
		break;
		/* Future controllers can be added here */

	default:
		printf("Invalid controller name passed. Setting controller"
					" failed. Dependent tests may fail\n");
		return 1;
		break;
	}
}

/**
 * Tests if a group has been modified
 * @param path_control_file path to the control file of the controller
 * @param value_type which value out of four types
 * @param struct cval the control value structure
 */
int group_modified(char *path_control_file, int value_type,
						 struct cntl_val_t cval)
{
	bool bool_val;
	int64_t int64_val;
	u_int64_t uint64_val;
	/* 100 char looks ok for a control value as string */
	char string_val[100];
	FILE *fd;
	int error = 1;
	int aux;

	fd = fopen(path_control_file, "r");
	if (!fd) {
		fprintf(stderr, "Error in opening %s\n", path_control_file);
		fprintf(stderr, "Skipping modified values check....\n");
		return 1;
	}

	switch (value_type) {

	case BOOL:
		fscanf(fd, "%d", &aux);
		bool_val = aux;
		if (bool_val == cval.val_bool)
			error = 0;
		break;
	case INT64:
		fscanf(fd, "%" SCNi64, &int64_val);
		if (int64_val == cval.val_int64)
			error = 0;
		break;
	case UINT64:
		fscanf(fd, "%" SCNu64, &uint64_val);
		if (uint64_val == cval.val_uint64)
			error = 0;
		break;
	case STRING:
		fscanf(fd, "%s", string_val);
		if (!strncmp(string_val, cval.val_string, strlen(string_val)))
			error = 0;
		break;
	default:
		fprintf(stderr, "Wrong value_type passed "
						"in group_modified()\n");
		fprintf(stderr, "Skipping modified values check....\n");
		/* Can not report test result as failure */
		error = 0;
		break;
	}

	fclose(fd);
	return error;
}

/**
 * Adds the control value to a controller using wrapper apis
 * @param newcontroller the controller to be added the value to
 * @param control_file name of the control file of the controller
 * @param wr the name of wrapper api
 * @param value_type which value out of four types
 * @param struct cval the control value structure
 */
int add_control_value(struct cgroup_controller *newcontroller,
	 char *control_file, char *wr, int value_type, struct cntl_val_t cval)
{
	int retval;

	switch (value_type) {

	case BOOL:
		retval = cgroup_add_value_bool(newcontroller,
					 control_file, cval.val_bool);
		snprintf(wr, SIZE, "add_value_bool()");
		break;
	case INT64:
		retval = cgroup_add_value_int64(newcontroller,
					 control_file, cval.val_int64);
		snprintf(wr, SIZE, "add_value_int64()");
		break;
	case UINT64:
		retval = cgroup_add_value_uint64(newcontroller,
					 control_file, cval.val_uint64);
		snprintf(wr, SIZE, "add_value_uint64()");
		break;
	case STRING:
		retval = cgroup_add_value_string(newcontroller,
					 control_file, cval.val_string);
		snprintf(wr, SIZE, "add_value_string()");
		break;
	default:
		printf("ERROR: wrong value in add_control_value()\n");
		return 1;
		break;
	}
	return retval;
}

/**
 * This function creates and returns a cgroup data structure
 * @param group the name of the group
 * @param controller_name the name of the controller to be added to the group
 * @param control_file name of the control file of the controller
 * @param value_type which value out of four types
 * @param struct cval the control value structure
 * @param struct ids the permissions struct
 * @param the test number
 */
struct cgroup *new_cgroup(char *group, char *controller_name,
			 char *control_file, int value_type,
			 struct cntl_val_t cval, struct uid_gid_t ids, int i)
{
	int retval;
	/* Names of wrapper apis */
	char wr[SIZE];
	struct cgroup *newcgroup;
	struct cgroup_controller *newcontroller;

	newcgroup = cgroup_new_cgroup(group);

	if (newcgroup) {
		retval = cgroup_set_uid_gid(newcgroup, ids.tasks_uid,
			 ids.tasks_gid,	ids.control_uid, ids.control_gid);

		if (retval) {
			snprintf(wr, SIZE, "set_uid_gid()");
			message(i++, FAIL, wr, retval, info[NOMESSAGE]);
		}

		newcontroller = cgroup_add_controller(newcgroup,
							 controller_name);
		if (newcontroller) {
			retval =  add_control_value(newcontroller,
					 control_file, wr, value_type, cval);

			if (!retval) {
				message(i++, PASS, "new_cgroup()",
						 retval, info[NOMESSAGE]);
			} else {
				message(i++, FAIL, wr, retval ,
							 info[NOMESSAGE]);
				cgroup_free(&newcgroup);
				return NULL;
			}
		 } else {
			/* Since these wrappers do not return an int so -1 */
			message(i++, FAIL, "add_controller", -1,
							 info[NOMESSAGE]);
			cgroup_free(&newcgroup);
			return NULL;
		}
	} else {
		message(i++, FAIL, "new_cgroup", -1, info[NOMESSAGE]);
		return NULL;
	}
	return newcgroup;
}

/**
 * Checks if the cgroup filesystem has been mounted
 * @param multimnt to decide if check is for single mount or multimount
 */
int check_fsmounted(int multimnt)
{
	int count = 0;
	int ret = 1;
	struct mntent *entry = NULL, *tmp_entry = NULL;
	/* Need a better mechanism to decide memory allocation size here */
	char entry_buffer[FILENAME_MAX * 4];
	FILE *proc_file = NULL;

	tmp_entry = (struct mntent *) malloc(sizeof(struct mntent));
	if (!tmp_entry) {
		perror("Error: failled to mallloc for mntent\n");
		ret = errno;
		goto error;
	}

	proc_file = fopen("/proc/mounts", "r");
	if (!proc_file) {
		printf("Error in opening /proc/mounts.\n");
		ret = errno;
		goto error;
	}
	while ((entry = getmntent_r(proc_file, tmp_entry, entry_buffer,
						 FILENAME_MAX*4)) != NULL) {
		if (!strncmp(entry->mnt_type, "cgroup", strlen("cgroup"))) {
			count++;
			if (multimnt) {
				if (count >= 2) {
					printf("sanity check pass. %s\n",
							 entry->mnt_type);
					ret = 0;
					goto error;
				}
			} else {
				printf("sanity check pass. %s\n",
							 entry->mnt_type);
				ret = 0;
				goto error;
			}
		}
	}
error:
	if (tmp_entry)
		free(tmp_entry);
	if (proc_file)
		fclose(proc_file);
	return ret;
}

/**
 * Checks if the current task belongs to the given tasks file
 * @param tasksfile the task file to be tested for the task
 */
int check_task(char *tasksfile, pid_t pid)
{
	FILE *file;
	pid_t curr_tid, tid;
	int pass = 0;

	file = fopen(tasksfile, "r");
	if (!file) {
		printf("ERROR: in opening %s\n", tasksfile);
		printf("Exiting without running other testcases in this set\n");
		exit(1);
	}

	if (pid)
		curr_tid = pid;
	else
		curr_tid = cgrouptest_gettid();

	while (!feof(file)) {
		fscanf(file, "%u", &tid);
		if (tid == curr_tid) {
			pass = 1;
			break;
		}
	}
	fclose(file);

	return pass;
}

/**
 * Prints the test result in a readable format with some verbose messages
 * @param num the test number
 * @param pass test passed or failed
 * @param api the name of the api tested
 * @param retval the return value of the api
 * @param extra the extra message to the user about the scenario tested
 */
void message(int num, int pass, const char *api, int retval, char *extra)
{
	char res[10];
	char buf[2*SIZE];
	if (pass)
		strncpy(res, "PASS :", 10);
	else
		strncpy(res, "FAIL :", 10);

	/* Populate message buffer for the api */
	snprintf(buf, sizeof(buf), "cgroup_%s\t\t Ret Value = ", api);
	fprintf(stdout, "TEST%2d:%s %s%d\t%s", num, res, buf, retval, extra);
}

/**
 * Builds the path to target file/group
 * @param target to write the built path to
 * @param mountpoint for which mountpoint the path to be built
 * @param group the name of the group (directory)
 * @param file what file under the group
 */
void
build_path(char *target, char *mountpoint, const char *group, const char *file)
{
	if (!target)
		return;

	strncpy(target, mountpoint, FILENAME_MAX);

	if (group) {
		strncat(target, "/", FILENAME_MAX - strlen(target));
		strncat(target, group, FILENAME_MAX - strlen(target));
	}

	if (file) {
		strncat(target, "/", FILENAME_MAX - strlen(target));
		strncat(target, file, FILENAME_MAX - strlen(target));
	}
}

/**
 * Tests the cgroup_compare_cgroup() api under different scenarios
 * @param ctl1 controller 1 to be used for testing
 * @param ctl2 controller 1 to be used for testing
 * @param the test number
 */
void test_cgroup_compare_cgroup(int ctl1, int ctl2, int i)
{
	int retval;

	struct cntl_val_t cval;
	cval.val_int64 = 0;
	cval.val_uint64 = 0;
	cval.val_bool = 0;
	strcpy(cval.val_string, "5000");

	struct cgroup *cgroup1 = NULL, *cgroup2 = NULL;
	struct cgroup_controller *controller = NULL;
	char controller_name[FILENAME_MAX], control_file[FILENAME_MAX];
	char wr[SIZE], extra[] = "in cgroup_compare_cgroup";

	retval = cgroup_compare_cgroup(NULL, NULL);
	if (retval)
		message(i++, PASS, "compare_cgroup()", retval, info[NULLGRP]);
	else
		message(i++, FAIL, "compare_cgroup()", retval, info[NULLGRP]);

	cgroup1 = cgroup_new_cgroup("testgroup");
	cgroup2 = cgroup_new_cgroup("testgroup");
	cgroup_set_uid_gid(cgroup1, 0, 0, 0, 0);
	cgroup_set_uid_gid(cgroup2, 0, 0, 0, 0);

	retval = set_controller(ctl1, controller_name, control_file);

	controller = cgroup_add_controller(cgroup1, controller_name);
	if (controller) {
		retval =  add_control_value(controller,
					 control_file, wr, STRING, cval);
		if (retval)
			message(i++, FAIL, wr, retval, extra);
	}

	controller = cgroup_add_controller(cgroup2, controller_name);
	if (controller) {
		retval =  add_control_value(controller,
					 control_file, wr, STRING, cval);
		if (retval)
			message(i++, FAIL, wr, retval, extra);
	}

	retval = cgroup_compare_cgroup(cgroup1, cgroup2);
	if (retval)
		message(i++, FAIL, "compare_cgroup()", retval, info[NOMESSAGE]);
	else
		message(i++, PASS, "compare_cgroup()", retval, info[NOMESSAGE]);

	/* Test the api by putting diff number of controllers in cgroups */
	retval = set_controller(ctl2, controller_name, control_file);
	controller = cgroup_add_controller(cgroup2, controller_name);
	if (controller) {
		retval =  add_control_value(controller,
					 control_file, wr, STRING, cval);
		if (retval)
			message(i++, FAIL, wr, retval, extra);
	}

	retval = cgroup_compare_cgroup(cgroup1, cgroup2);
	if (retval == ECGROUPNOTEQUAL)
		message(i++, PASS, "compare_cgroup()", retval, info[NOMESSAGE]);
	else
		message(i++, FAIL, "compare_cgroup()", retval, info[NOMESSAGE]);

	cgroup_free(&cgroup1);
	cgroup_free(&cgroup2);
}

/**
 * Tests the cgroup_get_cgroup() api under different scenarios
 * @param ctl1 controller 1 to be used for testing
 * @param ctl2 controller 1 to be used for testing
 * @param struct ids the permissions struct
 * @param the test number
 */
void test_cgroup_get_cgroup(int ctl1, int ctl2, struct uid_gid_t ids, int i)
{
	struct cgroup *cgroup_filled = NULL, *cgroup_a = NULL, *cgroup_b = NULL;
	struct cgroup_controller *controller = NULL;
	char controller_name[FILENAME_MAX], control_file[FILENAME_MAX];
	struct cntl_val_t cval = {0, 0, 0, "5000"};
	int ret;

	/*
	 * No need to test the next 3 scenarios separately for Multimnt
	 * so testing them only under single mount
	 */
	if (fs_mounted == FS_MOUNTED) {
		/* 1. Test with nullcgroup first */
		ret = cgroup_get_cgroup(NULL);
		if (ret == ECGROUPNOTALLOWED)
			message(i++, PASS, "get_cgroup()", ret, info[NULLGRP]);
		else
			message(i++, FAIL, "get_cgroup()", ret, info[NULLGRP]);

		/* 2. Test with invalid name filled cgroup(non existing) */
		cgroup_filled = cgroup_new_cgroup("nogroup");
		if (!cgroup_filled)
			message(i++, FAIL, "new_cgroup()", 0, info[NOMESSAGE]);

		ret = cgroup_get_cgroup(cgroup_filled);
		if (ret)
			message(i++, PASS, "get_cgroup()", ret,
							 info[NOTCRTDGRP]);
		else
			message(i++, FAIL, "get_cgroup()", ret,
							 info[NOTCRTDGRP]);
		/* Free the allocated cgroup before reallocation */
		cgroup_free(&cgroup_filled);

		/* 3.
		 * Test with name filled cgroup. Ensure the group group1 exists
		 * in the filesystem before calling this test function
		 */
		cgroup_filled = cgroup_new_cgroup("group1");
		if (!cgroup_filled)
			message(i++, FAIL, "new_cgroup()", 0, info[NOMESSAGE]);

		ret = cgroup_get_cgroup(cgroup_filled);
		if (!ret)
			message(i++, PASS, "get_cgroup()", ret,
							 info[NOMESSAGE]);
		else
			message(i++, FAIL, "get_cgroup()", ret,
							 info[NOMESSAGE]);
	}

	/* SINGLE & MULTI MOUNT: Create, get and compare a cgroup */

	/* get cgroup_a ds and create group_a in filesystem */
	cgroup_a = create_new_cgroup_ds(ctl1, "group_a", STRING, cval, ids, 0);
	if (fs_mounted == FS_MULTI_MOUNTED) {
		/* Create under another controller also */
		ret = set_controller(ctl2, controller_name, control_file);
		controller = cgroup_add_controller(cgroup_a, controller_name);
		if (controller)
			message(i++, PASS, "cgroup_add_controller()",
					0, info[NOMESSAGE]);
		else
			message(i++, FAIL, "cgroup_add_controller()",
					-1, info[NOMESSAGE]);
	}
	test_cgroup_create_cgroup(0, cgroup_a, "group_a", 0, 1, 1, 00);

	/* create group_b ds to be filled by cgroup_get_cgroup */
	cgroup_b = cgroup_new_cgroup("group_a");
	if (!cgroup_b)
		message(i++, FAIL, "new_cgroup()", 0, info[NOMESSAGE]);
	/* Fill the ds and compare the two */
	ret = cgroup_get_cgroup(cgroup_b);
	if (!ret) {
		ret = cgroup_compare_cgroup(cgroup_a, cgroup_b);
		if (ret == 0)
			message(i++, PASS, "get_cgroup()", ret, info[SAMEGRP]);
		else
			message(i++, FAIL, "get_cgroup()", ret,
							 info[NOMESSAGE]);
	} else {
		message(i++, FAIL, "get_cgroup()", ret, info[NOMESSAGE]);
	}

	/* Delete this created group from fs to leave fs clean */
	if (fs_mounted == FS_MULTI_MOUNTED)
		test_cgroup_delete_cgroup(0, cgroup_a, "group_a", 1, 1, 0, 0);
	else
		test_cgroup_delete_cgroup(0, cgroup_a, "group_a", 0, 1, 0, 0);

	cgroup_free(&cgroup_a);
	cgroup_free(&cgroup_b);
	cgroup_free(&cgroup_filled);
}

/**
 * Tests the cgroup_add_controller() and cgroup_free_controller() wrapper
 * apis under different scenarios
 * @param the test number
 */
void test_cgroup_add_free_controller(int i)
{
	struct cgroup *cgroup1 = NULL, *cgroup2 = NULL;
	struct cgroup_controller *cgctl1, *cgctl2;

	/* Test with a Null cgroup */
	cgctl1 = cgroup_add_controller(cgroup1, "cpu");
	if (!cgctl1)
		message(i++, PASS, "add_controller()", 0, info[NOMESSAGE]);
	else
		message(i++, FAIL, "add_controller()", -1, info[NOMESSAGE]);

	cgroup1 = cgroup_new_cgroup("testgroup");
	cgctl1 = cgroup_add_controller(cgroup1, "cpuset");
	if (cgctl1)
		message(i++, PASS, "add_controller()", 0, info[NOMESSAGE]);
	else
		message(i++, FAIL, "add_controller()", -1, info[NOMESSAGE]);

	cgctl2 = cgroup_add_controller(cgroup1, "cpu");
	if (cgctl2)
		message(i++, PASS, "add_controller()", 0, info[NOMESSAGE]);
	else
		message(i++, FAIL, "add_controller()", -1, info[NOMESSAGE]);

	cgroup_free(&cgroup1);
	cgroup_free_controllers(cgroup2);
}

/**
 * Returns the tid of the current thread
 */
pid_t cgrouptest_gettid()
{
	return syscall(__NR_gettid);
}
