<commit_before>//
//  NotificationPayload.cpp
//
//  Created by Juan V. Guerrero on 9/29/11.
//  Copyright (c) 2011 fn(x) Software. All rights reserved.
//
#include "NotificationPayload.h"
#include "UtilityFunctions.h"
#include "GenericException.h"
#include <iostream>
#include <sstream>
#ifndef MAXPAYLOAD_SIZE
#define MAXPAYLOAD_SIZE 256
#endif

namespace fnx {

	static void msg_encode(std::string &theMsg){
		std::string newString;
		for (size_t i = 0; i < theMsg.size(); i++) {
			if (theMsg[i] == '\n') {
				newString.append("\\n");
			}
			else if (theMsg[i] == '\r') {
				newString.append("\\r");
			}
			else if (theMsg[i] == '\t') {
				newString.append("\\t");
			}
			else if (theMsg[i] == '\"') {
				newString.append("\\\"");
			}
			else if (theMsg[i] == '\\') {
				newString.append("\\\\");
			}
			else if (theMsg[i] == '\'') {
				newString.append("\\'");
			}
			else {
				char tbuf[2] = { theMsg[i], 0x00};
				newString.append(tbuf);
			}
		}
		theMsg = newString;
	}

	NotificationPayload::NotificationPayload(){
		_badgeNumber = 1;
		attempts = 0;
	}

	NotificationPayload::NotificationPayload(DeviceType devType, const std::string &devToken_, const std::string &_message, int badgeNumber, const std::string &sndName){
		msg = _message;
		_soundName = sndName;
		devToken = devToken_;
		_badgeNumber = badgeNumber;
		attempts = 0;
        deviceType = devType;
		build();
	}

	NotificationPayload::NotificationPayload(const NotificationPayload &n){
		msg = n.msg;
		_soundName = n._soundName;
		devToken = n.devToken;
		_badgeNumber = n._badgeNumber;
        deviceType = n.deviceType;
        metadata = n.metadata;
		build();
		attempts = n.attempts;
	}

	NotificationPayload::~NotificationPayload(){

	}

    void NotificationPayload::iosBuild() {
		std::stringstream jsonbuilder;
		std::string encodedMsg = msg;
		msg_encode(encodedMsg);
		jsonbuilder << "{";
		jsonbuilder << "\"aps\":";
		jsonbuilder << "{";
		jsonbuilder << "\"alert\":\"" << encodedMsg << "\",";
		jsonbuilder << "\"sound\":\"" << _soundName << "\"";
		if(_badgeNumber > 0) jsonbuilder << ",\"badge\":" << _badgeNumber;
		jsonbuilder << "}";
        //Begin metadata insert
        for (std::map<std::string, std::string>::iterator iter = metadata.begin(); iter != metadata.end(); iter++) {
            jsonbuilder << ",\"" << iter->first << "\":\"" << iter->second << "\"";
        }
        //End metadata insert
		jsonbuilder << "}";
		if (jsonbuilder.str().size() > MAXPAYLOAD_SIZE) {
			jsonbuilder.str(std::string());
			jsonbuilder << "{";
			jsonbuilder << "\"aps\":";
			jsonbuilder << "{";
			jsonbuilder << "\"sound\":\"" << _soundName << "\"";
			if(_badgeNumber > 0) jsonbuilder << ",\"badge\":" << _badgeNumber;
			jsonbuilder << "}";
			jsonbuilder << "}";
			size_t maxMsgLength = jsonbuilder.str().size() - 3;
			jsonbuilder.str(std::string());
			jsonbuilder << "{";
			jsonbuilder << "\"aps\":";
			jsonbuilder << "{";
			jsonbuilder << "\"alert\":\"" << encodedMsg.substr(0, maxMsgLength) << "...\",";
			jsonbuilder << "\"sound\":\"" << _soundName << "\"";
			if(_badgeNumber > 0) jsonbuilder << ",\"badge\":" << _badgeNumber;
			jsonbuilder << "}";
            //Begin metadata insert
            for (std::map<std::string, std::string>::iterator iter = metadata.begin(); iter != metadata.end(); iter++) {
                jsonbuilder << ",\"" << iter->first << "\":\"" << iter->second << "\"";
            }
            //End metadata insert
			jsonbuilder << "}";
		}
		msgCloudRepresentation = jsonbuilder.str();

    }

    void NotificationPayload::androidBuild() {
        throw GenericException("Unable to build Android notification payloads, the functionality has not been implemented.");
    }

	void NotificationPayload::build(){
        switch (deviceType) {
            case IOS:
                iosBuild();
                break;
            case ANDROID:
                androidBuild();
                break;
            default:
                throw GenericException("Unable to build notification payload for unidentified device type. Current supported device types are IOS and ANDROID only.");
                break;
        }
	}

	const std::string &NotificationPayload::toCloudFormat() const {
		return msgCloudRepresentation;
	}

	std::string &NotificationPayload::soundName(){
		return _soundName;
	}

	const std::string &NotificationPayload::soundName() const {
		return _soundName;
	}

	std::string &NotificationPayload::message(){
		return msg;
	}

	std::string &NotificationPayload::deviceToken(){
		return devToken;
	}

	int NotificationPayload::badge(){
		return _badgeNumber;
	}

    NotificationPayload::DeviceType NotificationPayload::devType() {
        return deviceType;
    }

    void NotificationPayload::setMetadata(const std::map<std::string, std::string> &meta) {
        metadata = meta;
        build();
    }
}
<commit_msg>Fixing alert message truncating logic<commit_after>//
//  NotificationPayload.cpp
//
//  Created by Juan V. Guerrero on 9/29/11.
//  Copyright (c) 2011 fn(x) Software. All rights reserved.
//
#include "NotificationPayload.h"
#include "UtilityFunctions.h"
#include "GenericException.h"
#include <iostream>
#include <sstream>
#ifndef MAXPAYLOAD_SIZE
#define MAXPAYLOAD_SIZE 256
#endif

namespace fnx {

	static void msg_encode(std::string &theMsg){
		std::string newString;
		for (size_t i = 0; i < theMsg.size(); i++) {
			if (theMsg[i] == '\n') {
				newString.append("\\n");
			}
			else if (theMsg[i] == '\r') {
				newString.append("\\r");
			}
			else if (theMsg[i] == '\t') {
				newString.append("\\t");
			}
			else if (theMsg[i] == '\"') {
				newString.append("\\\"");
			}
			else if (theMsg[i] == '\\') {
				newString.append("\\\\");
			}
			else if (theMsg[i] == '\'') {
				newString.append("\\'");
			}
			else {
				char tbuf[2] = { theMsg[i], 0x00};
				newString.append(tbuf);
			}
		}
		theMsg = newString;
	}

	NotificationPayload::NotificationPayload(){
		_badgeNumber = 1;
		attempts = 0;
	}

	NotificationPayload::NotificationPayload(DeviceType devType, const std::string &devToken_, const std::string &_message, int badgeNumber, const std::string &sndName){
		msg = _message;
		_soundName = sndName;
		devToken = devToken_;
		_badgeNumber = badgeNumber;
		attempts = 0;
        deviceType = devType;
		build();
	}

	NotificationPayload::NotificationPayload(const NotificationPayload &n){
		msg = n.msg;
		_soundName = n._soundName;
		devToken = n.devToken;
		_badgeNumber = n._badgeNumber;
        deviceType = n.deviceType;
        metadata = n.metadata;
		build();
		attempts = n.attempts;
	}

	NotificationPayload::~NotificationPayload(){

	}

    void NotificationPayload::iosBuild() {
		std::stringstream jsonbuilder;
		std::string encodedMsg = msg;
		msg_encode(encodedMsg);
		jsonbuilder << "{";
		jsonbuilder << "\"aps\":";
		jsonbuilder << "{";
		jsonbuilder << "\"alert\":\"" << encodedMsg << "\",";
		jsonbuilder << "\"sound\":\"" << _soundName << "\"";
		if(_badgeNumber > 0) jsonbuilder << ",\"badge\":" << _badgeNumber;
		jsonbuilder << "}";
        //Begin metadata insert
        for (std::map<std::string, std::string>::iterator iter = metadata.begin(); iter != metadata.end(); iter++) {
            jsonbuilder << ",\"" << iter->first << "\":\"" << iter->second << "\"";
        }
        //End metadata insert
		jsonbuilder << "}";
		if (jsonbuilder.str().size() > MAXPAYLOAD_SIZE) {
			jsonbuilder.str(std::string());
			jsonbuilder << "{";
			jsonbuilder << "\"aps\":";
			jsonbuilder << "{";
			jsonbuilder << "\"sound\":\"" << _soundName << "\"";
			if(_badgeNumber > 0) jsonbuilder << ",\"badge\":" << _badgeNumber;
			jsonbuilder << "}";
			jsonbuilder << "}";

			size_t maxMsgLength = jsonbuilder.str().size() - 3;
			jsonbuilder.str(std::string());
			jsonbuilder << "{";
			jsonbuilder << "\"aps\":";
			jsonbuilder << "{";
			jsonbuilder << "\"alert\":\"" << encodedMsg.substr(0, 252 - maxMsgLength) << "...\",";
			jsonbuilder << "\"sound\":\"" << _soundName << "\"";
			if(_badgeNumber > 0) jsonbuilder << ",\"badge\":" << _badgeNumber;
			jsonbuilder << "}";
            //Begin metadata insert
            for (std::map<std::string, std::string>::iterator iter = metadata.begin(); iter != metadata.end(); iter++) {
                jsonbuilder << ",\"" << iter->first << "\":\"" << iter->second << "\"";
            }
            //End metadata insert
			jsonbuilder << "}";
		}
		msgCloudRepresentation = jsonbuilder.str();

    }

    void NotificationPayload::androidBuild() {
        throw GenericException("Unable to build Android notification payloads, the functionality has not been implemented.");
    }

	void NotificationPayload::build(){
        switch (deviceType) {
            case IOS:
                iosBuild();
                break;
            case ANDROID:
                androidBuild();
                break;
            default:
                throw GenericException("Unable to build notification payload for unidentified device type. Current supported device types are IOS and ANDROID only.");
                break;
        }
	}

	const std::string &NotificationPayload::toCloudFormat() const {
		return msgCloudRepresentation;
	}

	std::string &NotificationPayload::soundName(){
		return _soundName;
	}

	const std::string &NotificationPayload::soundName() const {
		return _soundName;
	}

	std::string &NotificationPayload::message(){
		return msg;
	}

	std::string &NotificationPayload::deviceToken(){
		return devToken;
	}

	int NotificationPayload::badge(){
		return _badgeNumber;
	}

    NotificationPayload::DeviceType NotificationPayload::devType() {
        return deviceType;
    }

    void NotificationPayload::setMetadata(const std::map<std::string, std::string> &meta) {
        metadata = meta;
        build();
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015, Ford Motor Company
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of the Ford Motor Company nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <string>
#include <algorithm>
#include "json/value.h"
#include "gtest/gtest.h"
#include "json/reader.h"
#include "formatters/CFormatterJsonBase.hpp"
#include "formatters/generic_json_formatter.h"

namespace test {
namespace components {
namespace formatters {

using namespace NsSmartDeviceLink::NsSmartObjects;
using namespace NsSmartDeviceLink::NsJSONHandler::Formatters;

TEST(CFormatterJsonBaseTest, JSonStringValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  std::string string_val("test_string");
  Json::Value json_value(string_val);  // Json value from string
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(string_val, object.asString());
}

TEST(CFormatterJsonBaseTest, JSonDoubleValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  double dval = 3.512;
  Json::Value json_value(dval);  // Json value from double
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_DOUBLE_EQ(dval, object.asDouble());
}

TEST(CFormatterJsonBaseTest, JSonMinIntValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::Value::minInt;
  Json::Value json_value(ival);  // Json value from possible minimum signed int
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(ival, object.asInt());
}

TEST(CFormatterJsonBaseTest, JSonNullIntValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::nullValue;
  Json::Value json_value(ival);  // Json value from null int value
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(ival, object.asInt());
}

TEST(CFormatterJsonBaseTest, JSonSignedMaxIntValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::Value::maxInt;
  Json::Value json_value(ival);  // Json value from maximum possible signed int
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(ival, object.asInt());
}

TEST(CFormatterJsonBaseTest, DISABLED_JSonUnsignedMaxIntValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  Json::UInt ui_val = Json::Value::maxUInt;
  Json::Value json_value(ui_val);  // Json value from maximum possible unsigned int
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(ui_val, object.asUInt());
}

TEST(CFormatterJsonBaseTest, JSonSignedMaxInt64ValueToSmartObj_ExpectFailed) {
  // Arrange value
  Json::Int64 ival = Json::Value::maxInt64;
  Json::Value json_value(ival);  // Json value from maximum possible signed int
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was not successful as there is no such conversion
  EXPECT_EQ(invalid_int64_value, object.asInt64());
}

TEST(CFormatterJsonBaseTest, JSonBoolValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  bool bval1 = true;
  bool bval2 = false;
  Json::Value json_value1(bval1);  // Json value from bool
  Json::Value json_value2(bval2);  // Json value from bool
  SmartObject object1;
  SmartObject object2;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value1, object1);
  CFormatterJsonBase::jsonValueToObj(json_value2, object2);
  // Check conversion was successful
  EXPECT_TRUE(object1.asBool());
  EXPECT_FALSE(object2.asBool());
}

TEST(CFormatterJsonBaseTest, JSonCStringValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  const char* cstr_val = "cstring_test";
  Json::Value json_value(cstr_val);  // Json value from const char*
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_STREQ(cstr_val, object.asCharArray());
}

TEST(CFormatterJsonBaseTest, JSonArrayValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  const char* json_array = "[\"test1\", \"test2\", \"test3\"]";  // Array in json format
  Json::Value json_value;  // Json value from array. Will be initialized later
  SmartObject object;
  Json::Reader reader;  // Json reader - Needed for correct parsing
  // Parse array to json value
  ASSERT_TRUE(reader.parse(json_array, json_value));
  // Convert json array to SmartObject
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_TRUE(json_value.isArray());
  EXPECT_EQ(3u, object.asArray()->size());
  SmartArray *ptr = NULL;  // Smart Array pointer;
  EXPECT_NE(ptr, object.asArray());
}

TEST(CFormatterJsonBaseTest, JSonObjectValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  const char* json_object =
      "{ \"json_test_object\": [\"test1\", \"test2\", \"test3\"], \"json_test_object2\": [\"test11\", \"test12\", \"test13\" ]}";  // Json object
  Json::Value json_value;  // Json value from object. Will be initialized later
  SmartObject object;
  Json::Reader reader;  // Json reader - Needed for correct parsing
  ASSERT_TRUE(reader.parse(json_object, json_value));  // If parsing not successful - no sense to continue
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_TRUE(json_value.isObject());
  EXPECT_TRUE(json_value.type() == Json::objectValue);
  // Get keys collection from Smart Object
  std::set<std::string> keys = object.enumerate();
  std::set<std::string>::iterator it1 = keys.begin();
  // Get members names(keys) from Json object
  Json::Value::Members mems = json_value.getMemberNames();
  std::vector<std::string>::iterator it;
  // Compare sizes
  EXPECT_EQ(mems.size(), keys.size());
  // Sort mems
  std::sort(mems.begin(), mems.end());
  // Full data compare
  for (it = mems.begin(); it != mems.end(); ++it) {
    EXPECT_EQ(*it, *it1);
    ++it1;
  }
  ASSERT(it == mems.end() && it1 == keys.end());
}

TEST(CFormatterJsonBaseTest, StringSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  std::string string_val("test_string");
  SmartObject object(string_val);
  Json::Value json_value;  // Json value from string
  // Convert smart object to json
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_EQ(string_val, json_value.asString());
}

TEST(CFormatterJsonBaseTest, DoubleSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  double dval = 3.512;
  Json::Value json_value;  // Json value from double
  SmartObject object(dval);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_DOUBLE_EQ(dval, json_value.asDouble());
}

TEST(CFormatterJsonBaseTest, ZeroIntSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::nullValue;
  Json::Value json_value;  // Json value from zero int
  SmartObject object(ival);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_EQ(ival, json_value.asInt());
}

TEST(CFormatterJsonBaseTest, MinIntSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::Value::minInt;
  Json::Value json_value;  // Json value from mimimum possible signed int
  SmartObject object(ival);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_EQ(ival, json_value.asInt());
}

TEST(CFormatterJsonBaseTest, DISABLED_UnsignedMaxIntSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  Json::UInt ui_val = Json::Value::maxUInt;
  Json::Value json_value;  // Json value from maximum unsigned int
  SmartObject object(ui_val);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_EQ(ui_val, json_value.asUInt());
}

TEST(CFormatterJsonBaseTest, BoolSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  bool bval1 = true;
  bool bval2 = false;
  Json::Value json_value1;  // Json value from bool
  Json::Value json_value2;  // Json value from bool
  SmartObject object1(bval1);
  SmartObject object2(bval2);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object1, json_value1);
  CFormatterJsonBase::objToJsonValue(object2, json_value2);
  // Check conversion was successful
  EXPECT_TRUE(json_value1.asBool());
  EXPECT_FALSE(json_value2.asBool());
}

TEST(CFormatterJsonBaseTest, CStringSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  const char* cstr_val = "cstring_test";
  Json::Value json_value;  // Json value from const char*
  SmartObject object(cstr_val);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_STREQ(cstr_val, json_value.asCString());
}

TEST(CFormatterJsonBaseTest, ArraySmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  const char* json_array = "[\"test1\", \"test2\", \"test3\"]";  // Array in json format
  Json::Value json_value;  // Json value from array. Will be initialized later
  Json::Value result;  // Json value from array. Will be initialized later
  SmartObject object;
  Json::Reader reader;  // Json reader - Needed for correct parsing
  // Parse array to json value
  ASSERT_TRUE(reader.parse(json_array, json_value));  // Convert json array to SmartObject
  // Convert json array to SmartObject
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Convert SmartObject to JSon
  CFormatterJsonBase::objToJsonValue(object, result);
  // Check conversion was successful
  EXPECT_TRUE(result.isArray());
  EXPECT_EQ(3u, result.size());
}

TEST(CFormatterJsonBaseTest, JSonObjectValueToObj_ExpectSuccessful) {
  // Arrange value
  const char* json_object =
      "{ \"json_test_object\": [\"test1\", \"test2\", \"test3\"], \"json_test_object2\": [\"test11\", \"test12\", \"test13\" ]}";  // Json object
  Json::Value json_value;  // Json value from json object. Will be initialized later
  Json::Value result;  // Json value from Smart object. Will keep conversion result
  SmartObject object;
  Json::Reader reader;  // Json reader - Needed for correct parsing
  // Parse json object to correct json value
  ASSERT_TRUE(reader.parse(json_object, json_value));  // If parsing not successful - no sense to continue
  // Convert json array to SmartObject
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Convert SmartObject to JSon
  CFormatterJsonBase::objToJsonValue(object, result);
  // Check conversion was successful
  EXPECT_TRUE(result.isObject());
  EXPECT_TRUE(result.type() == Json::objectValue);
  EXPECT_TRUE(result == json_value);
  // Get keys collection from Smart Object
  std::set<std::string> keys = object.enumerate();
  std::set<std::string>::iterator it1 = keys.begin();
  // Get members names(keys) from Json object
  Json::Value::Members mems = result.getMemberNames();
  std::vector<std::string>::iterator it;
  // Compare sizes
  EXPECT_EQ(mems.size(), keys.size());
  // Sort mems
  std::sort(mems.begin(), mems.end());
  // Full data compare
  for (it = mems.begin(); it != mems.end(); ++it) {
    EXPECT_EQ(*it, *it1);
    ++it1;
  }
  ASSERT(it == mems.end() && it1 == keys.end());
}

}  // namespace formatters
}  // namespace components
}  // namespace test
<commit_msg>hotfix/Fix_Unit_Tests_for_Formatter_in_Genivi<commit_after>/*
 * Copyright (c) 2015, Ford Motor Company
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of the Ford Motor Company nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <string>
#include <algorithm>
#include "json/value.h"
#include "gtest/gtest.h"
#include "json/reader.h"
#include "formatters/CFormatterJsonBase.hpp"
#include "formatters/generic_json_formatter.h"

namespace test {
namespace components {
namespace formatters {

using namespace NsSmartDeviceLink::NsSmartObjects;
using namespace NsSmartDeviceLink::NsJSONHandler::Formatters;

TEST(CFormatterJsonBaseTest, JSonStringValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  std::string string_val("test_string");
  Json::Value json_value(string_val);  // Json value from string
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(string_val, object.asString());
}

TEST(CFormatterJsonBaseTest, JSonDoubleValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  double dval = 3.512;
  Json::Value json_value(dval);  // Json value from double
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_DOUBLE_EQ(dval, object.asDouble());
}

TEST(CFormatterJsonBaseTest, JSonMinIntValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::Value::minInt;
  Json::Value json_value(ival);  // Json value from possible minimum signed int
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(ival, object.asInt());
}

TEST(CFormatterJsonBaseTest, JSonNullIntValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::nullValue;
  Json::Value json_value(ival);  // Json value from null int value
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(ival, object.asInt());
}

TEST(CFormatterJsonBaseTest, JSonSignedMaxIntValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::Value::maxInt;
  Json::Value json_value(ival);  // Json value from maximum possible signed int
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(ival, object.asInt());
}

TEST(CFormatterJsonBaseTest, JSonUnsignedMaxIntValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  Json::Int ui_val = Json::Value::maxInt;
  Json::Value json_value(ui_val);  // Json value from maximum possible unsigned int
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_EQ(ui_val, object.asInt());
}

TEST(CFormatterJsonBaseTest, JSonSignedMaxInt64ValueToSmartObj_ExpectFailed) {
  // Arrange value
  Json::Int64 ival = Json::Value::maxInt64;
  Json::Value json_value(ival);  // Json value from maximum possible signed int
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was not successful as there is no such conversion
  EXPECT_EQ(invalid_int64_value, object.asInt64());
}

TEST(CFormatterJsonBaseTest, JSonBoolValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  bool bval1 = true;
  bool bval2 = false;
  Json::Value json_value1(bval1);  // Json value from bool
  Json::Value json_value2(bval2);  // Json value from bool
  SmartObject object1;
  SmartObject object2;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value1, object1);
  CFormatterJsonBase::jsonValueToObj(json_value2, object2);
  // Check conversion was successful
  EXPECT_TRUE(object1.asBool());
  EXPECT_FALSE(object2.asBool());
}

TEST(CFormatterJsonBaseTest, JSonCStringValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  const char* cstr_val = "cstring_test";
  Json::Value json_value(cstr_val);  // Json value from const char*
  SmartObject object;
  // Convert json to smart object
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_STREQ(cstr_val, object.asCharArray());
}

TEST(CFormatterJsonBaseTest, JSonArrayValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  const char* json_array = "[\"test1\", \"test2\", \"test3\"]";  // Array in json format
  Json::Value json_value;  // Json value from array. Will be initialized later
  SmartObject object;
  Json::Reader reader;  // Json reader - Needed for correct parsing
  // Parse array to json value
  ASSERT_TRUE(reader.parse(json_array, json_value));
  // Convert json array to SmartObject
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_TRUE(json_value.isArray());
  EXPECT_EQ(3u, object.asArray()->size());
  SmartArray *ptr = NULL;  // Smart Array pointer;
  EXPECT_NE(ptr, object.asArray());
}

TEST(CFormatterJsonBaseTest, JSonObjectValueToSmartObj_ExpectSuccessful) {
  // Arrange value
  const char* json_object =
      "{ \"json_test_object\": [\"test1\", \"test2\", \"test3\"], \"json_test_object2\": [\"test11\", \"test12\", \"test13\" ]}";  // Json object
  Json::Value json_value;  // Json value from object. Will be initialized later
  SmartObject object;
  Json::Reader reader;  // Json reader - Needed for correct parsing
  ASSERT_TRUE(reader.parse(json_object, json_value));  // If parsing not successful - no sense to continue
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Check conversion was successful
  EXPECT_TRUE(json_value.isObject());
  EXPECT_TRUE(json_value.type() == Json::objectValue);
  // Get keys collection from Smart Object
  std::set<std::string> keys = object.enumerate();
  std::set<std::string>::iterator it1 = keys.begin();
  // Get members names(keys) from Json object
  Json::Value::Members mems = json_value.getMemberNames();
  std::vector<std::string>::iterator it;
  // Compare sizes
  EXPECT_EQ(mems.size(), keys.size());
  // Sort mems
  std::sort(mems.begin(), mems.end());
  // Full data compare
  for (it = mems.begin(); it != mems.end(); ++it) {
    EXPECT_EQ(*it, *it1);
    ++it1;
  }
  ASSERT(it == mems.end() && it1 == keys.end());
}

TEST(CFormatterJsonBaseTest, StringSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  std::string string_val("test_string");
  SmartObject object(string_val);
  Json::Value json_value;  // Json value from string
  // Convert smart object to json
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_EQ(string_val, json_value.asString());
}

TEST(CFormatterJsonBaseTest, DoubleSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  double dval = 3.512;
  Json::Value json_value;  // Json value from double
  SmartObject object(dval);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_DOUBLE_EQ(dval, json_value.asDouble());
}

TEST(CFormatterJsonBaseTest, ZeroIntSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::nullValue;
  Json::Value json_value;  // Json value from zero int
  SmartObject object(ival);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_EQ(ival, json_value.asInt());
}

TEST(CFormatterJsonBaseTest, MinIntSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  Json::Int ival = Json::Value::minInt;
  Json::Value json_value;  // Json value from mimimum possible signed int
  SmartObject object(ival);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_EQ(ival, json_value.asInt());
}

TEST(CFormatterJsonBaseTest, UnsignedMaxIntSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  Json::Int ui_val = Json::Value::maxInt;
  Json::Value json_value;  // Json value from maximum unsigned int
  SmartObject object(ui_val);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_EQ(ui_val, json_value.asInt());
}

TEST(CFormatterJsonBaseTest, BoolSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  bool bval1 = true;
  bool bval2 = false;
  Json::Value json_value1;  // Json value from bool
  Json::Value json_value2;  // Json value from bool
  SmartObject object1(bval1);
  SmartObject object2(bval2);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object1, json_value1);
  CFormatterJsonBase::objToJsonValue(object2, json_value2);
  // Check conversion was successful
  EXPECT_TRUE(json_value1.asBool());
  EXPECT_FALSE(json_value2.asBool());
}

TEST(CFormatterJsonBaseTest, CStringSmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  const char* cstr_val = "cstring_test";
  Json::Value json_value;  // Json value from const char*
  SmartObject object(cstr_val);
  // Convert json to smart object
  CFormatterJsonBase::objToJsonValue(object, json_value);
  // Check conversion was successful
  EXPECT_STREQ(cstr_val, json_value.asCString());
}

TEST(CFormatterJsonBaseTest, ArraySmartObjectToJSon_ExpectSuccessful) {
  // Arrange value
  const char* json_array = "[\"test1\", \"test2\", \"test3\"]";  // Array in json format
  Json::Value json_value;  // Json value from array. Will be initialized later
  Json::Value result;  // Json value from array. Will be initialized later
  SmartObject object;
  Json::Reader reader;  // Json reader - Needed for correct parsing
  // Parse array to json value
  ASSERT_TRUE(reader.parse(json_array, json_value));  // Convert json array to SmartObject
  // Convert json array to SmartObject
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Convert SmartObject to JSon
  CFormatterJsonBase::objToJsonValue(object, result);
  // Check conversion was successful
  EXPECT_TRUE(result.isArray());
  EXPECT_EQ(3u, result.size());
}

TEST(CFormatterJsonBaseTest, JSonObjectValueToObj_ExpectSuccessful) {
  // Arrange value
  const char* json_object =
      "{ \"json_test_object\": [\"test1\", \"test2\", \"test3\"], \"json_test_object2\": [\"test11\", \"test12\", \"test13\" ]}";  // Json object
  Json::Value json_value;  // Json value from json object. Will be initialized later
  Json::Value result;  // Json value from Smart object. Will keep conversion result
  SmartObject object;
  Json::Reader reader;  // Json reader - Needed for correct parsing
  // Parse json object to correct json value
  ASSERT_TRUE(reader.parse(json_object, json_value));  // If parsing not successful - no sense to continue
  // Convert json array to SmartObject
  CFormatterJsonBase::jsonValueToObj(json_value, object);
  // Convert SmartObject to JSon
  CFormatterJsonBase::objToJsonValue(object, result);
  // Check conversion was successful
  EXPECT_TRUE(result.isObject());
  EXPECT_TRUE(result.type() == Json::objectValue);
  EXPECT_TRUE(result == json_value);
  // Get keys collection from Smart Object
  std::set<std::string> keys = object.enumerate();
  std::set<std::string>::iterator it1 = keys.begin();
  // Get members names(keys) from Json object
  Json::Value::Members mems = result.getMemberNames();
  std::vector<std::string>::iterator it;
  // Compare sizes
  EXPECT_EQ(mems.size(), keys.size());
  // Sort mems
  std::sort(mems.begin(), mems.end());
  // Full data compare
  for (it = mems.begin(); it != mems.end(); ++it) {
    EXPECT_EQ(*it, *it1);
    ++it1;
  }
  ASSERT(it == mems.end() && it1 == keys.end());
}

}  // namespace formatters
}  // namespace components
}  // namespace test
<|endoftext|>


<commit_before>#include "oemhandler.hpp"
#include "config.h"
#include "elog-errors.hpp"
#include <host-ipmid/ipmid-api.h>
#include <host-ipmid/ipmid-host-cmd.hpp>
#include <fstream>
#include <memory>
#include <stdio.h>
#include <string.h>
#include <endian.h>
#include <functional>
#include <systemd/sd-bus.h>
#include <sdbusplus/bus.hpp>
#include <host-interface.hpp>
#include <org/open_power/OCC/Metrics/error.hpp>

void register_netfn_oem_partial_esel() __attribute__((constructor));

const char *g_esel_path = "/tmp/esel";
uint16_t g_record_id = 0x0001;
using namespace phosphor::logging;
constexpr auto occMetricsType = 0xDD;

std::string readESEL(const char* fileName)
{
    std::string content {};

    std::ifstream handle(fileName);

    if (handle.fail())
    {
        log<level::ERR>("Failed to open eSEL",
                entry("FILENAME=%s", fileName));
        return content;
    }

    handle.seekg(0, std::ios::end);
    content.resize(handle.tellg());
    handle.seekg(0, std::ios::beg);
    handle.read(&content[0], content.size());
    handle.close();

    return content;
}

void createOCCLogEntry(const std::string& eSELData)
{
    // Each byte in eSEL is formatted as %02x with a space between bytes and
    // insert '/0' at the end of the character array.
    constexpr auto byteSeperator = 3;

    std::unique_ptr<char[]> data(new char[
        (eSELData.size() * byteSeperator) + 1]());

    for (size_t i = 0; i < eSELData.size(); i++)
    {
        sprintf(&data[i * byteSeperator], "%02x ", eSELData[i]);
    }
    data[eSELData.size() * byteSeperator] = '\0';

    using error =  sdbusplus::org::open_power::OCC::Metrics::Error::Event;
    using metadata = org::open_power::OCC::Metrics::Event;

    report<error>(metadata::ESEL(data.get()));
}

///////////////////////////////////////////////////////////////////////////////
// For the First partial add eSEL the SEL Record ID and offset
// value should be 0x0000. The extended data needs to be in
// the form of an IPMI SEL Event Record, with Event sensor type
// of 0xDF and Event Message format of 0x04. The returned
// Record ID should be used for all partial eSEL adds.
//
// This function creates a /tmp/esel file to store the
// incoming partial esel.  It is the role of some other
// function to commit the error log in to long term
// storage.  Likely via the ipmi add_sel command.
///////////////////////////////////////////////////////////////////////////////
ipmi_ret_t ipmi_ibm_oem_partial_esel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
                                     ipmi_request_t request, ipmi_response_t response,
                                     ipmi_data_len_t data_len, ipmi_context_t context)
{
	uint8_t *reqptr = (uint8_t *) request;
	esel_request_t esel_req;
	FILE *fp;
	int r = 0;
	uint8_t rlen;
	ipmi_ret_t rc = IPMI_CC_OK;
	const char *pio;

	esel_req.resid     = le16toh((((uint16_t) reqptr[1]) << 8) + reqptr[0]);
	esel_req.selrecord = le16toh((((uint16_t) reqptr[3]) << 8) + reqptr[2]);
	esel_req.offset    = le16toh((((uint16_t) reqptr[5]) << 8) + reqptr[4]);
	esel_req.progress  = reqptr[6];

	uint16_t used_res_id = get_sel_reserve_id();

	// According to IPMI spec, Reservation ID must be checked.
	if ( used_res_id != esel_req.resid ) {
		// 0xc5 means Reservation Cancelled or Invalid Reservation ID.
		printf("Used Reservation ID = %d\n", used_res_id);
		rc = IPMI_CC_INVALID_RESERVATION_ID;

		// clean g_esel_path.
		r = remove(g_esel_path);
		if(r < 0)
			fprintf(stderr, "Error deleting %s\n", g_esel_path);

		return rc;
	}

    // OpenPOWER Host Interface spec says if RecordID and Offset are
	// 0 then then this is a new request
	if (!esel_req.selrecord && !esel_req.offset)
		pio = "wb";
	else
		pio = "rb+";

	rlen = (*data_len) - (uint8_t) (sizeof(esel_request_t));

	if ((fp = fopen(g_esel_path, pio)) != NULL) {
		fseek(fp, esel_req.offset, SEEK_SET);
		fwrite(reqptr+(uint8_t) (sizeof(esel_request_t)), rlen, 1, fp);
		fclose(fp);

		*data_len = sizeof(g_record_id);
		memcpy(response, &g_record_id, *data_len);
	} else {
		fprintf(stderr, "Error trying to perform %s for esel%s\n",pio, g_esel_path);
		rc = IPMI_CC_INVALID;
		*data_len     = 0;
	}

	// The first bit presents that this is the last partial packet
	// coming down.  If that is the case advance the record id so we
	// don't overlap logs.  This allows anyone to establish a log
	// directory system.
    if (esel_req.progress & 1 )
    {
         g_record_id++;

         auto eSELData = readESEL(g_esel_path);

         if (eSELData.empty())
         {
             return IPMI_CC_UNSPECIFIED_ERROR;
         }

         // If the eSEL record type is OCC metrics, then create the OCC log
         // entry.
         if (eSELData[2] == occMetricsType)
         {
             createOCCLogEntry(eSELData);
         }
    }

    return rc;
}

// Prepare for FW Update.
// Execute needed commands to prepare the system for a fw update from the host.
ipmi_ret_t ipmi_ibm_oem_prep_fw_update(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
                                     ipmi_request_t request, ipmi_response_t response,
                                     ipmi_data_len_t data_len, ipmi_context_t context)
{
    ipmi_ret_t ipmi_rc = IPMI_CC_OK;
    *data_len = 0;

    int rc = 0;
    std::ofstream rwfs_file;
    const char  *busname = "org.openbmc.control.Bmc";
    const char  *objname = "/org/openbmc/control/bmc0";
    const char  *iface = "org.openbmc.control.Bmc";
    sd_bus *bus = ipmid_get_sd_bus_connection();
    sd_bus_message *reply = NULL;
    sd_bus_error error = SD_BUS_ERROR_NULL;
    int r = 0;

    // Set one time flag
    rc = system("fw_setenv openbmconce copy-files-to-ram copy-base-filesystem-to-ram");
    rc = WEXITSTATUS(rc);
    if (rc != 0) {
        fprintf(stderr, "fw_setenv openbmconce failed with rc=%d\n", rc);
        return IPMI_CC_UNSPECIFIED_ERROR;
    }

    // Touch the image-rwfs file to perform an empty update to force the save
    // in case we're already in ram and the flash is the same causing the ram files
    // to not be copied back to flash
    rwfs_file.open("/run/initramfs/image-rwfs", std::ofstream::out | std::ofstream::app);
    rwfs_file.close();

    // Reboot the BMC for settings to take effect
    r = sd_bus_call_method(bus, busname, objname, iface,
                           "warmReset", &error, &reply, NULL);
    if (r < 0) {
        fprintf(stderr, "Failed to reset BMC: %s\n", strerror(-r));
        return -1;
    }
    printf("Warning: BMC is going down for reboot!\n");
    sd_bus_error_free(&error);
    reply = sd_bus_message_unref(reply);

    return ipmi_rc;
}

namespace {
// Storage to keep the object alive during process life
std::unique_ptr<open_power::host::command::Host> opHost
        __attribute__((init_priority(101)));
std::unique_ptr<sdbusplus::server::manager::manager> objManager
        __attribute__((init_priority(101)));
}

void register_netfn_oem_partial_esel()
{
    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_IBM_OEM, IPMI_CMD_PESEL);
    ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_PESEL, NULL, ipmi_ibm_oem_partial_esel,
                           SYSTEM_INTERFACE);

    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE);
    ipmi_register_callback(NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE, NULL, ipmi_ibm_oem_prep_fw_update,
                           SYSTEM_INTERFACE);

    // Create new object on the bus
    auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';

    // Add sdbusplus ObjectManager.
    auto& sdbusPlusHandler = ipmid_get_sdbus_plus_handler();
    objManager = std::make_unique<sdbusplus::server::manager::manager>(
                    *sdbusPlusHandler,
                    CONTROL_HOST_OBJ_MGR);

    opHost = std::make_unique<open_power::host::command::Host>(
                        *sdbusPlusHandler, objPath.c_str());

    // Service for this is provided by phosphor layer systemcmdintf
    // and this will be as part of that.
    return;
}
<commit_msg>oemhandler: Rename constructor function for generality<commit_after>#include "oemhandler.hpp"
#include "config.h"
#include "elog-errors.hpp"
#include <host-ipmid/ipmid-api.h>
#include <host-ipmid/ipmid-host-cmd.hpp>
#include <fstream>
#include <memory>
#include <stdio.h>
#include <string.h>
#include <endian.h>
#include <functional>
#include <systemd/sd-bus.h>
#include <sdbusplus/bus.hpp>
#include <host-interface.hpp>
#include <org/open_power/OCC/Metrics/error.hpp>

void register_netfn_ibm_oem_commands() __attribute__((constructor));

const char *g_esel_path = "/tmp/esel";
uint16_t g_record_id = 0x0001;
using namespace phosphor::logging;
constexpr auto occMetricsType = 0xDD;

std::string readESEL(const char* fileName)
{
    std::string content {};

    std::ifstream handle(fileName);

    if (handle.fail())
    {
        log<level::ERR>("Failed to open eSEL",
                entry("FILENAME=%s", fileName));
        return content;
    }

    handle.seekg(0, std::ios::end);
    content.resize(handle.tellg());
    handle.seekg(0, std::ios::beg);
    handle.read(&content[0], content.size());
    handle.close();

    return content;
}

void createOCCLogEntry(const std::string& eSELData)
{
    // Each byte in eSEL is formatted as %02x with a space between bytes and
    // insert '/0' at the end of the character array.
    constexpr auto byteSeperator = 3;

    std::unique_ptr<char[]> data(new char[
        (eSELData.size() * byteSeperator) + 1]());

    for (size_t i = 0; i < eSELData.size(); i++)
    {
        sprintf(&data[i * byteSeperator], "%02x ", eSELData[i]);
    }
    data[eSELData.size() * byteSeperator] = '\0';

    using error =  sdbusplus::org::open_power::OCC::Metrics::Error::Event;
    using metadata = org::open_power::OCC::Metrics::Event;

    report<error>(metadata::ESEL(data.get()));
}

///////////////////////////////////////////////////////////////////////////////
// For the First partial add eSEL the SEL Record ID and offset
// value should be 0x0000. The extended data needs to be in
// the form of an IPMI SEL Event Record, with Event sensor type
// of 0xDF and Event Message format of 0x04. The returned
// Record ID should be used for all partial eSEL adds.
//
// This function creates a /tmp/esel file to store the
// incoming partial esel.  It is the role of some other
// function to commit the error log in to long term
// storage.  Likely via the ipmi add_sel command.
///////////////////////////////////////////////////////////////////////////////
ipmi_ret_t ipmi_ibm_oem_partial_esel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
                                     ipmi_request_t request, ipmi_response_t response,
                                     ipmi_data_len_t data_len, ipmi_context_t context)
{
	uint8_t *reqptr = (uint8_t *) request;
	esel_request_t esel_req;
	FILE *fp;
	int r = 0;
	uint8_t rlen;
	ipmi_ret_t rc = IPMI_CC_OK;
	const char *pio;

	esel_req.resid     = le16toh((((uint16_t) reqptr[1]) << 8) + reqptr[0]);
	esel_req.selrecord = le16toh((((uint16_t) reqptr[3]) << 8) + reqptr[2]);
	esel_req.offset    = le16toh((((uint16_t) reqptr[5]) << 8) + reqptr[4]);
	esel_req.progress  = reqptr[6];

	uint16_t used_res_id = get_sel_reserve_id();

	// According to IPMI spec, Reservation ID must be checked.
	if ( used_res_id != esel_req.resid ) {
		// 0xc5 means Reservation Cancelled or Invalid Reservation ID.
		printf("Used Reservation ID = %d\n", used_res_id);
		rc = IPMI_CC_INVALID_RESERVATION_ID;

		// clean g_esel_path.
		r = remove(g_esel_path);
		if(r < 0)
			fprintf(stderr, "Error deleting %s\n", g_esel_path);

		return rc;
	}

    // OpenPOWER Host Interface spec says if RecordID and Offset are
	// 0 then then this is a new request
	if (!esel_req.selrecord && !esel_req.offset)
		pio = "wb";
	else
		pio = "rb+";

	rlen = (*data_len) - (uint8_t) (sizeof(esel_request_t));

	if ((fp = fopen(g_esel_path, pio)) != NULL) {
		fseek(fp, esel_req.offset, SEEK_SET);
		fwrite(reqptr+(uint8_t) (sizeof(esel_request_t)), rlen, 1, fp);
		fclose(fp);

		*data_len = sizeof(g_record_id);
		memcpy(response, &g_record_id, *data_len);
	} else {
		fprintf(stderr, "Error trying to perform %s for esel%s\n",pio, g_esel_path);
		rc = IPMI_CC_INVALID;
		*data_len     = 0;
	}

	// The first bit presents that this is the last partial packet
	// coming down.  If that is the case advance the record id so we
	// don't overlap logs.  This allows anyone to establish a log
	// directory system.
    if (esel_req.progress & 1 )
    {
         g_record_id++;

         auto eSELData = readESEL(g_esel_path);

         if (eSELData.empty())
         {
             return IPMI_CC_UNSPECIFIED_ERROR;
         }

         // If the eSEL record type is OCC metrics, then create the OCC log
         // entry.
         if (eSELData[2] == occMetricsType)
         {
             createOCCLogEntry(eSELData);
         }
    }

    return rc;
}

// Prepare for FW Update.
// Execute needed commands to prepare the system for a fw update from the host.
ipmi_ret_t ipmi_ibm_oem_prep_fw_update(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
                                     ipmi_request_t request, ipmi_response_t response,
                                     ipmi_data_len_t data_len, ipmi_context_t context)
{
    ipmi_ret_t ipmi_rc = IPMI_CC_OK;
    *data_len = 0;

    int rc = 0;
    std::ofstream rwfs_file;
    const char  *busname = "org.openbmc.control.Bmc";
    const char  *objname = "/org/openbmc/control/bmc0";
    const char  *iface = "org.openbmc.control.Bmc";
    sd_bus *bus = ipmid_get_sd_bus_connection();
    sd_bus_message *reply = NULL;
    sd_bus_error error = SD_BUS_ERROR_NULL;
    int r = 0;

    // Set one time flag
    rc = system("fw_setenv openbmconce copy-files-to-ram copy-base-filesystem-to-ram");
    rc = WEXITSTATUS(rc);
    if (rc != 0) {
        fprintf(stderr, "fw_setenv openbmconce failed with rc=%d\n", rc);
        return IPMI_CC_UNSPECIFIED_ERROR;
    }

    // Touch the image-rwfs file to perform an empty update to force the save
    // in case we're already in ram and the flash is the same causing the ram files
    // to not be copied back to flash
    rwfs_file.open("/run/initramfs/image-rwfs", std::ofstream::out | std::ofstream::app);
    rwfs_file.close();

    // Reboot the BMC for settings to take effect
    r = sd_bus_call_method(bus, busname, objname, iface,
                           "warmReset", &error, &reply, NULL);
    if (r < 0) {
        fprintf(stderr, "Failed to reset BMC: %s\n", strerror(-r));
        return -1;
    }
    printf("Warning: BMC is going down for reboot!\n");
    sd_bus_error_free(&error);
    reply = sd_bus_message_unref(reply);

    return ipmi_rc;
}

namespace {
// Storage to keep the object alive during process life
std::unique_ptr<open_power::host::command::Host> opHost
        __attribute__((init_priority(101)));
std::unique_ptr<sdbusplus::server::manager::manager> objManager
        __attribute__((init_priority(101)));
}

void register_netfn_ibm_oem_commands()
{
    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_IBM_OEM, IPMI_CMD_PESEL);
    ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_PESEL, NULL, ipmi_ibm_oem_partial_esel,
                           SYSTEM_INTERFACE);

    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE);
    ipmi_register_callback(NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE, NULL, ipmi_ibm_oem_prep_fw_update,
                           SYSTEM_INTERFACE);

    // Create new object on the bus
    auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';

    // Add sdbusplus ObjectManager.
    auto& sdbusPlusHandler = ipmid_get_sdbus_plus_handler();
    objManager = std::make_unique<sdbusplus::server::manager::manager>(
                    *sdbusPlusHandler,
                    CONTROL_HOST_OBJ_MGR);

    opHost = std::make_unique<open_power::host::command::Host>(
                        *sdbusPlusHandler, objPath.c_str());

    // Service for this is provided by phosphor layer systemcmdintf
    // and this will be as part of that.
    return;
}
<|endoftext|>


<commit_before>/**
 * @author Andre Anjos <andre.anjos@idiap.ch>
 * @date Thu  7 Nov 13:50:16 2013
 *
 * @brief Binds configuration information available from bob
 */

#include <xbob.core/config.h>

#define MODULE_NAME "_externals"
#define LIBRARY_NAME _externals

#include <bob/config.h>

#include <string>
#include <cstdlib>
#include <blitz/blitz.h>
#include <boost/preprocessor/stringize.hpp>
#include <boost/version.hpp>
#include <boost/format.hpp>
#if WITH_PERFTOOLS
#include <google/tcmalloc.h>
#endif

extern "C" {

#include <Python.h>

#ifdef NO_IMPORT_ARRAY
#undef NO_IMPORT_ARRAY
#endif
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>

}

static int dict_set(PyObject* d, const char* key, const char* value) {
  PyObject* v = Py_BuildValue("s", value);
  if (!v) return 0;
  int retval = PyDict_SetItemString(d, key, v);
  Py_DECREF(v);
  if (retval == 0) return 1; //all good
  return 0; //a problem occurred
}

static int dict_steal(PyObject* d, const char* key, PyObject* value) {
  if (!value) return 0;
  int retval = PyDict_SetItemString(d, key, value);
  Py_DECREF(value);
  if (retval == 0) return 1; //all good
  return 0; //a problem occurred
}

/**
 * Describes the version of Boost libraries installed
 */
static PyObject* boost_version() {
  boost::format f("%d.%d.%d");
  f % (BOOST_VERSION / 100000);
  f % (BOOST_VERSION / 100 % 1000);
  f % (BOOST_VERSION % 100);
  return Py_BuildValue("s", f.str().c_str());
}

/**
 * Describes the compiler version
 */
static PyObject* compiler_version() {
# if defined(__GNUC__) && !defined(__llvm__)
  boost::format f("%s.%s.%s");
  f % BOOST_PP_STRINGIZE(__GNUC__);
  f % BOOST_PP_STRINGIZE(__GNUC_MINOR__);
  f % BOOST_PP_STRINGIZE(__GNUC_PATCHLEVEL__);
  return Py_BuildValue("ss", "gcc", f.str().c_str());
# elif defined(__llvm__) && !defined(__clang__)
  return Py_BuildValue("ss", "llvm-gcc", __VERSION__);
# elif defined(__clang__)
  return Py_BuildValue("ss", "clang", __clang_version__);
# else
  return Py_BuildValue("s", "unsupported");
# endif
}

/**
 * Python version with which we compiled the extensions
 */
static PyObject* python_version() {
  boost::format f("%s.%s.%s");
  f % BOOST_PP_STRINGIZE(PY_MAJOR_VERSION);
  f % BOOST_PP_STRINGIZE(PY_MINOR_VERSION);
  f % BOOST_PP_STRINGIZE(PY_MICRO_VERSION);
  return Py_BuildValue("s", f.str().c_str());
}

/**
 * Numpy version
 */
static PyObject* numpy_version() {
  return Py_BuildValue("s", BOOST_PP_STRINGIZE(NPY_VERSION));
}

/**
 * Google profiler version, if available
 */
static PyObject* perftools_version() {
#if WITH_PERFTOOLS
  boost::format f("%s.%s.%s");
  f % BOOST_PP_STRINGIZE(TC_VERSION_MAJOR);
  f % BOOST_PP_STRINGIZE(TC_VERSION_MINOR);
  if (std::strlen(TC_VERSION_PATCH) == 0) f % "0";
  else f % BOOST_PP_STRINGIZE(TC_VERSION_PATCH);
  return Py_BuildValue("s", f.str().c_str());
#else
  return Py_BuildValue("s", "unavailable");
#endif
}

/**
 * Bob version, API version and platform
 */
static PyObject* bob_version() {
  return Py_BuildValue("sis", BOB_VERSION, BOB_API_VERSION, BOB_PLATFORM);
}

static PyObject* build_version_dictionary() {

  PyObject* retval = PyDict_New();
  if (!retval) return 0;

  if (!dict_set(retval, "Blitz++", BZ_VERSION)) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Boost", boost_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Compiler", compiler_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Python", python_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "NumPy", numpy_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Google Perftools", perftools_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Bob", bob_version())) {
    Py_DECREF(retval);
    return 0;
  }

  return retval;
}

static PyMethodDef module_methods[] = {
    {0}  /* Sentinel */
};

PyDoc_STRVAR(module_docstr,
"Information about software used to compile the C++ Bob API"
);

PyMODINIT_FUNC XBOB_EXT_ENTRY_NAME (void) {

  PyObject* m = Py_InitModule3(MODULE_NAME, module_methods, module_docstr);

  /* register some constants */
  PyModule_AddIntConstant(m, "__api_version__", XBOB_CORE_API_VERSION);
  PyModule_AddStringConstant(m, "__version__", XBOB_EXT_MODULE_VERSION);
  PyModule_AddObject(m, "versions", build_version_dictionary());

  /* imports the NumPy C-API */
  import_array();

}
<commit_msg>Remove local macros<commit_after>/**
 * @author Andre Anjos <andre.anjos@idiap.ch>
 * @date Thu  7 Nov 13:50:16 2013
 *
 * @brief Binds configuration information available from bob
 */

#include <xbob.core/config.h>

#include <bob/config.h>

#include <string>
#include <cstdlib>
#include <blitz/blitz.h>
#include <boost/preprocessor/stringize.hpp>
#include <boost/version.hpp>
#include <boost/format.hpp>
#if WITH_PERFTOOLS
#include <google/tcmalloc.h>
#endif

extern "C" {

#include <Python.h>

#ifdef NO_IMPORT_ARRAY
#undef NO_IMPORT_ARRAY
#endif
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>

}

static int dict_set(PyObject* d, const char* key, const char* value) {
  PyObject* v = Py_BuildValue("s", value);
  if (!v) return 0;
  int retval = PyDict_SetItemString(d, key, v);
  Py_DECREF(v);
  if (retval == 0) return 1; //all good
  return 0; //a problem occurred
}

static int dict_steal(PyObject* d, const char* key, PyObject* value) {
  if (!value) return 0;
  int retval = PyDict_SetItemString(d, key, value);
  Py_DECREF(value);
  if (retval == 0) return 1; //all good
  return 0; //a problem occurred
}

/**
 * Describes the version of Boost libraries installed
 */
static PyObject* boost_version() {
  boost::format f("%d.%d.%d");
  f % (BOOST_VERSION / 100000);
  f % (BOOST_VERSION / 100 % 1000);
  f % (BOOST_VERSION % 100);
  return Py_BuildValue("s", f.str().c_str());
}

/**
 * Describes the compiler version
 */
static PyObject* compiler_version() {
# if defined(__GNUC__) && !defined(__llvm__)
  boost::format f("%s.%s.%s");
  f % BOOST_PP_STRINGIZE(__GNUC__);
  f % BOOST_PP_STRINGIZE(__GNUC_MINOR__);
  f % BOOST_PP_STRINGIZE(__GNUC_PATCHLEVEL__);
  return Py_BuildValue("ss", "gcc", f.str().c_str());
# elif defined(__llvm__) && !defined(__clang__)
  return Py_BuildValue("ss", "llvm-gcc", __VERSION__);
# elif defined(__clang__)
  return Py_BuildValue("ss", "clang", __clang_version__);
# else
  return Py_BuildValue("s", "unsupported");
# endif
}

/**
 * Python version with which we compiled the extensions
 */
static PyObject* python_version() {
  boost::format f("%s.%s.%s");
  f % BOOST_PP_STRINGIZE(PY_MAJOR_VERSION);
  f % BOOST_PP_STRINGIZE(PY_MINOR_VERSION);
  f % BOOST_PP_STRINGIZE(PY_MICRO_VERSION);
  return Py_BuildValue("s", f.str().c_str());
}

/**
 * Numpy version
 */
static PyObject* numpy_version() {
  return Py_BuildValue("s", BOOST_PP_STRINGIZE(NPY_VERSION));
}

/**
 * Google profiler version, if available
 */
static PyObject* perftools_version() {
#if WITH_PERFTOOLS
  boost::format f("%s.%s.%s");
  f % BOOST_PP_STRINGIZE(TC_VERSION_MAJOR);
  f % BOOST_PP_STRINGIZE(TC_VERSION_MINOR);
  if (std::strlen(TC_VERSION_PATCH) == 0) f % "0";
  else f % BOOST_PP_STRINGIZE(TC_VERSION_PATCH);
  return Py_BuildValue("s", f.str().c_str());
#else
  return Py_BuildValue("s", "unavailable");
#endif
}

/**
 * Bob version, API version and platform
 */
static PyObject* bob_version() {
  return Py_BuildValue("sis", BOB_VERSION, BOB_API_VERSION, BOB_PLATFORM);
}

static PyObject* build_version_dictionary() {

  PyObject* retval = PyDict_New();
  if (!retval) return 0;

  if (!dict_set(retval, "Blitz++", BZ_VERSION)) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Boost", boost_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Compiler", compiler_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Python", python_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "NumPy", numpy_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Google Perftools", perftools_version())) {
    Py_DECREF(retval);
    return 0;
  }

  if (!dict_steal(retval, "Bob", bob_version())) {
    Py_DECREF(retval);
    return 0;
  }

  return retval;
}

static PyMethodDef module_methods[] = {
    {0}  /* Sentinel */
};

PyDoc_STRVAR(module_docstr,
"Information about software used to compile the C++ Bob API"
);

PyMODINIT_FUNC XBOB_EXT_ENTRY_NAME (void) {

  PyObject* m = Py_InitModule3(XBOB_EXT_MODULE_NAME, module_methods, module_docstr);

  /* register some constants */
  PyModule_AddIntConstant(m, "__api_version__", XBOB_CORE_API_VERSION);
  PyModule_AddStringConstant(m, "__version__", XBOB_EXT_MODULE_VERSION);
  PyModule_AddObject(m, "versions", build_version_dictionary());

  /* imports the NumPy C-API */
  import_array();

}
<|endoftext|>


<commit_before>// Copyright 2019 The Open GEE Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gtest/gtest.h>
#include <sys/stat.h>
#include <cstdlib>
#include <fstream>
#include <map>
#include <memory>
#include <sstream>
#include <string>

#include "khxml.h"
#include "khdom.h"

using namespace std;
using namespace khxml;

// These tests are focused on testing the Open GEE XML code, not the Xerces
// code. Thus, they do not try to hit every possible error condition; rather
// they hit at least one error condition in every function and make sure Open
// GEE handles it gracefully.
// The functions tested here are declared in khdom.h and defined in khxml.cpp.

const string TEST_DIR = "fusion/testdata/khxml";
const string VALID_XML = TEST_DIR + "/valid.xml";

class KhxmlTest : public ::testing::Test {
  protected:
    void SetUp() override {
      stringstream params;
      params << "INIT_HEAP_SIZE=16384" << endl
             << "MAX_HEAP_SIZE=131072" << endl
             << "BLOCK_SIZE=4096" << endl
             << "PURGE=1" << endl
             << "PURGE_LEVEL=1" << endl
             << "DEALLOCATE_ALL=1" << endl;
      GEXMLObject::initializeXMLParametersFromStream(params);
    }
};

void populateDocument(GEDocument * doc) {
  XMLCh value[] = {'t', 'e', 's', 't', '\0'};
  DOMElement * elem = doc->getDocumentElement();
  elem->appendChild(elem->getOwnerDocument()->createTextNode(value));
}

TEST_F(KhxmlTest, CreateAndWriteDocument)
{
  unique_ptr<GEDocument> doc = CreateEmptyDocument("rootTagname");
  ASSERT_NE(doc, nullptr) << "Failed to create empty document";

  // Do some basic operations and make sure they succeed
  populateDocument(doc.get());
  char * filename = tmpnam(nullptr);
  bool status = WriteDocument(doc.get(), filename);
  ASSERT_TRUE(status) << "Failed to write document";
}

TEST_F(KhxmlTest, CreateEmptyDocument_InvalidCharacter)
{
  unique_ptr<GEDocument> doc = CreateEmptyDocument("<<<<");
  ASSERT_EQ(doc, nullptr) << "Document creation with an invalid character should have failed";
}

TEST_F(KhxmlTest, CreateDocumentNoPerms) {
  unique_ptr<GEDocument> doc = CreateEmptyDocument("rootTagname");
  ASSERT_NE(doc, nullptr) << "Failed to create empty document";
  populateDocument(doc.get());

  // Try to write a document in a directory with insufficient permissions
  char dirTemplate[] = "/tmp/xmltestdirXXXXXX";
  string dir = mkdtemp(dirTemplate);
  string filename = dir + "/testfile";
  chmod(dir.c_str(), S_IROTH);

  bool status = WriteDocument(doc.get(), filename.c_str());
  ASSERT_FALSE(status) << "Should not have created document because of restrictive permissions";
}

TEST_F(KhxmlTest, ReadDocument) {
  unique_ptr<GEDocument> doc = ReadDocument(VALID_XML);
  ASSERT_NE(doc, nullptr) << "Error reading valid XML file";
}

TEST_F(KhxmlTest, ReadNonExistantDocument) {
  unique_ptr<GEDocument> doc = ReadDocument("notafile.xml");
  ASSERT_EQ(doc, nullptr) << "Reading a non-existant XML file should failed";
}

TEST_F(KhxmlTest, ReadFromString) {
  string docString =
      "<testfile>\n"
      "  <elem1>some data</elem1>\n"
      "  <elem2 attr=\"attribute\">other data</elem2>\n"
      "</testfile>";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(docString, "fake");
  ASSERT_NE(doc, nullptr) << "Could not read document from string";
}

// This test is disabled because I cannot find a way to get the parser to fail
// and throw an exception.
TEST_F(KhxmlTest, DISABLED_ReadInvalidDocFromString) {
  string docString =
      "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "(*@&#^*&@#";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(docString, "fake");
  ASSERT_EQ(doc, nullptr) << "Reading an invalid document from a string should have failed";
}

unique_ptr<GEDocument> getInvalidDocument() {
  return unique_ptr<GEDocument>(new GEParsedDocument("notafile.xml"));
}

TEST_F(KhxmlTest, GetElementFromInvalidDocument) {
  // Make sure that trying to read from an invalid document is handled gracefully
  unique_ptr<GEDocument> doc = getInvalidDocument();
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(elem, nullptr) << "An invalid document should not return an element";
}

TEST_F(KhxmlTest, WriteInvalidToString) {
  unique_ptr<GEDocument> doc = getInvalidDocument();
  string buf;
  bool status = WriteDocumentToString(doc.get(), buf);
  ASSERT_FALSE(status) << "Writing an invalid document to a string should fail";
}

TEST_F(KhxmlTest, WriteInvalidDocument) {
  unique_ptr<GEDocument> doc = getInvalidDocument();
  bool status = WriteDocument(doc.get(), TEST_DIR + "writeinvaliddocument.xml");
  ASSERT_FALSE(status) << "Writing an invalid document to a file should fail";
}

std::string getAttribute(DOMNode * child, const std::string & name) {
  DOMNamedNodeMap * attributes = child->getAttributes();
  if (!attributes) return "no attributes";
  if (attributes->getLength() != 1) return "wrong length";
  DOMNode * attr = attributes->getNamedItem(ToXMLStr(name));
  if (!attr) return "missing attribute";
  const XMLCh * value = attr->getNodeValue();
  if (!value) return "no value";
  return FromXMLStr(value);
}

int getValue(DOMNode * child) {
  const XMLCh * value = child->getFirstChild()->getNodeValue();
  if (!value) return -1;
  string valStr = FromXMLStr(value);
  return stoi(valStr);
}

template <class String>
void fromStringMapTest() {
  map<String, int> stringMap;
  stringMap["abc"] = 1;
  stringMap["def"] = 10;
  stringMap["ghi"] = 3;
  unique_ptr<GEDocument> doc = CreateEmptyDocument("test");
  DOMElement * elem = doc->getDocumentElement();
  AddElement(elem, "data", stringMap);

  DOMNodeList * dataElem = elem->getElementsByTagName(ToXMLStr("data"));
  ASSERT_EQ(dataElem->getLength(), 1) << "Wrong number of data elements extracted from XML";
  ASSERT_EQ(FromXMLStr(dataElem->item(0)->getNodeName()), "data") << "Unexpected tag name when extracting data element";
  DOMNode * child = dataElem->item(0)->getFirstChild();
  ASSERT_EQ(FromXMLStr(child->getNodeName()), "item") << "Invalid parent element when writing map of strings";
  ASSERT_EQ(getAttribute(child, "key"), "abc") << "Invalid key when writing map of strings";
  ASSERT_EQ(getValue(child), 1) << "Invalid value when writing map of strings";
  child = child->getNextSibling();
  ASSERT_EQ(FromXMLStr(child->getNodeName()), "item") << "Invalid parent element when writing map of strings";
  ASSERT_EQ(getAttribute(child, "key"), "def") << "Invalid key when writing map of strings";
  ASSERT_EQ(getValue(child), 10) << "Invalid value when writing map of strings";
  child = child->getNextSibling();
  ASSERT_EQ(FromXMLStr(child->getNodeName()), "item") << "Invalid parent element when writing map of strings";
  ASSERT_EQ(getAttribute(child, "key"), "ghi") << "Invalid key when writing map of strings";
  ASSERT_EQ(getValue(child), 3) << "Invalid value when writing map of strings";
  child = child->getNextSibling();
  ASSERT_EQ(child, nullptr) << "Unexpected element";
}

TEST_F(KhxmlTest, StdStringMapToElement) {
  fromStringMapTest<string>();
}

TEST_F(KhxmlTest, SharedStringMapToElement) {
  fromStringMapTest<SharedString>();
}

TEST_F(KhxmlTest, QStringMapToElement) {
  fromStringMapTest<QString>();
}

template <class String>
void toStringTest() {
  string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<test>expected_value</test>";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(xml, "dummy");
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(FromXMLStr(elem->getTagName()), "test") << "Invalid tag name when reading string from XML";
  String value;
  FromElement(elem, value);
  ASSERT_EQ(value, "expected_value") << "Could not read string value from XML correctly";
}

TEST_F(KhxmlTest, StdStringReadString) {
  toStringTest<string>();
}

TEST_F(KhxmlTest, SharedStringReadString) {
  toStringTest<SharedString>();
}

TEST_F(KhxmlTest, QStringReadString) {
  toStringTest<QString>();
}

template <class String>
void runXmlToStringMapTest(const std::string & xml) {
  unique_ptr<GEDocument> doc = ReadDocumentFromString(xml, "dummy");
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(FromXMLStr(elem->getTagName()), "data") << "Invalid tag name when reading string from XML";
  map<String, int> results;
  FromElement(elem, results);
  ASSERT_EQ(results["xyz"], 42) << "Error reading map of strings from XML";
  ASSERT_EQ(results["wvu"], 12) << "Error reading map of strings from XML";
}

template <class String>
void toStringMapTest() {
  // There are two methods of parsing maps from XML. We test both.
  string newxml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<data>\n"
         "<item key=\"xyz\">42</item>\n"
         "<item key=\"wvu\">12</item>\n"
      "</data>\n";
  runXmlToStringMapTest<String>(newxml);
  string oldxml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<data>\n"
        "<xyz>42</xyz>\n"
        "<wvu>12</wvu>\n"
      "</data>\n";
  runXmlToStringMapTest<String>(oldxml);
}

TEST_F(KhxmlTest, StdStringReadMap) {
  toStringMapTest<string>();
}

TEST_F(KhxmlTest, SharedStringReadMap) {
  toStringMapTest<SharedString>();
}

TEST_F(KhxmlTest, QStringReadMap) {
  toStringMapTest<QString>();
}

template <class String>
void readEmptyStringFromXml() {
  string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<value></value>\n";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(xml, "dummy");
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(FromXMLStr(elem->getTagName()), "value") << "Invalid tag name when reading empty string from XML";
  String val;
  FromElement(elem, val);
  ASSERT_EQ(val, String("")) << "Error reading empty string from XML";
}

TEST_F(KhxmlTest, StdStringReadEmpty) {
  readEmptyStringFromXml<string>();
}

TEST_F(KhxmlTest, SharedStringReadEmpty) {
  readEmptyStringFromXml<SharedString>();
}

TEST_F(KhxmlTest, QStringReadEmpty) {
  readEmptyStringFromXml<QString>();
}

template <class String>
void readNonEmptyStringFromXml() {
  string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<value>qwertyuiop</value>\n";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(xml, "dummy");
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(FromXMLStr(elem->getTagName()), "value") << "Invalid tag name when reading empty string from XML";
  String val;
  FromElement(elem, val);
  ASSERT_EQ(val, String("qwertyuiop")) << "Error reading empty string from XML";
}

TEST_F(KhxmlTest, StdStringReadNonEmpty) {
  readNonEmptyStringFromXml<string>();
}

TEST_F(KhxmlTest, SharedStringReadNonEmpty) {
  readNonEmptyStringFromXml<SharedString>();
}

TEST_F(KhxmlTest, QStringReadNonEmpty) {
  readNonEmptyStringFromXml<QString>();
}

int main(int argc, char** argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<commit_msg>Check for more error conditions<commit_after>// Copyright 2019 The Open GEE Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gtest/gtest.h>
#include <sys/stat.h>
#include <cstdlib>
#include <fstream>
#include <map>
#include <memory>
#include <sstream>
#include <string>

#include "khxml.h"
#include "khdom.h"

using namespace std;
using namespace khxml;

// These tests are focused on testing the Open GEE XML code, not the Xerces
// code. Thus, they do not try to hit every possible error condition; rather
// they hit at least one error condition in every function and make sure Open
// GEE handles it gracefully.
// The functions tested here are declared in khdom.h and defined in khxml.cpp.

const string TEST_DIR = "fusion/testdata/khxml";
const string VALID_XML = TEST_DIR + "/valid.xml";

class KhxmlTest : public ::testing::Test {
  protected:
    void SetUp() override {
      stringstream params;
      params << "INIT_HEAP_SIZE=16384" << endl
             << "MAX_HEAP_SIZE=131072" << endl
             << "BLOCK_SIZE=4096" << endl
             << "PURGE=1" << endl
             << "PURGE_LEVEL=1" << endl
             << "DEALLOCATE_ALL=1" << endl;
      GEXMLObject::initializeXMLParametersFromStream(params);
    }
};

void populateDocument(GEDocument * doc) {
  XMLCh value[] = {'t', 'e', 's', 't', '\0'};
  DOMElement * elem = doc->getDocumentElement();
  elem->appendChild(elem->getOwnerDocument()->createTextNode(value));
}

TEST_F(KhxmlTest, CreateAndWriteDocument)
{
  unique_ptr<GEDocument> doc = CreateEmptyDocument("rootTagname");
  ASSERT_NE(doc, nullptr) << "Failed to create empty document";

  // Do some basic operations and make sure they succeed
  populateDocument(doc.get());
  char * filename = tmpnam(nullptr);
  bool status = WriteDocument(doc.get(), filename);
  ASSERT_TRUE(status) << "Failed to write document";
}

TEST_F(KhxmlTest, CreateEmptyDocument_InvalidCharacter)
{
  unique_ptr<GEDocument> doc = CreateEmptyDocument("<<<<");
  ASSERT_EQ(doc, nullptr) << "Document creation with an invalid character should have failed";
}

TEST_F(KhxmlTest, CreateDocumentNoPerms) {
  unique_ptr<GEDocument> doc = CreateEmptyDocument("rootTagname");
  ASSERT_NE(doc, nullptr) << "Failed to create empty document";
  populateDocument(doc.get());

  // Try to write a document in a directory with insufficient permissions
  char dirTemplate[] = "/tmp/xmltestdirXXXXXX";
  string dir = mkdtemp(dirTemplate);
  string filename = dir + "/testfile";
  chmod(dir.c_str(), S_IROTH);

  bool status = WriteDocument(doc.get(), filename.c_str());
  ASSERT_FALSE(status) << "Should not have created document because of restrictive permissions";
}

TEST_F(KhxmlTest, ReadDocument) {
  unique_ptr<GEDocument> doc = ReadDocument(VALID_XML);
  ASSERT_NE(doc, nullptr) << "Error reading valid XML file";
}

TEST_F(KhxmlTest, ReadNonExistantDocument) {
  unique_ptr<GEDocument> doc = ReadDocument("notafile.xml");
  ASSERT_EQ(doc, nullptr) << "Reading a non-existant XML file should failed";
}

TEST_F(KhxmlTest, ReadFromString) {
  string docString =
      "<testfile>\n"
      "  <elem1>some data</elem1>\n"
      "  <elem2 attr=\"attribute\">other data</elem2>\n"
      "</testfile>";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(docString, "fake");
  ASSERT_NE(doc, nullptr) << "Could not read document from string";
}

// This test is disabled because I cannot find a way to get the parser to fail
// and throw an exception.
TEST_F(KhxmlTest, DISABLED_ReadInvalidDocFromString) {
  string docString =
      "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "(*@&#^*&@#";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(docString, "fake");
  ASSERT_EQ(doc, nullptr) << "Reading an invalid document from a string should have failed";
}

unique_ptr<GEDocument> getInvalidDocument() {
  return unique_ptr<GEDocument>(new GEParsedDocument("notafile.xml"));
}

TEST_F(KhxmlTest, GetElementFromInvalidDocument) {
  // Make sure that trying to read from an invalid document is handled gracefully
  unique_ptr<GEDocument> doc = getInvalidDocument();
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(elem, nullptr) << "An invalid document should not return an element";
}

TEST_F(KhxmlTest, WriteInvalidToString) {
  unique_ptr<GEDocument> doc = getInvalidDocument();
  string buf;
  bool status = WriteDocumentToString(doc.get(), buf);
  ASSERT_FALSE(status) << "Writing an invalid document to a string should fail";
}

TEST_F(KhxmlTest, WriteInvalidDocument) {
  unique_ptr<GEDocument> doc = getInvalidDocument();
  bool status = WriteDocument(doc.get(), TEST_DIR + "writeinvaliddocument.xml");
  ASSERT_FALSE(status) << "Writing an invalid document to a file should fail";
}

std::string getAttribute(DOMNode * child, const std::string & name) {
  DOMNamedNodeMap * attributes = child->getAttributes();
  if (!attributes) return "no attributes";
  if (attributes->getLength() != 1) return "wrong length";
  DOMNode * attr = attributes->getNamedItem(ToXMLStr(name));
  if (!attr) return "missing attribute";
  const XMLCh * value = attr->getNodeValue();
  if (!value) return "no value";
  return FromXMLStr(value);
}

int getValue(DOMNode * child) {
  DOMNode * grandchild = child->getFirstChild();
  if (!grandchild) return -2;
  const XMLCh * value = grandchild->getNodeValue();
  if (!value) return -1;
  string valStr = FromXMLStr(value);
  return stoi(valStr);
}

template <class String>
void fromStringMapTest() {
  map<String, int> stringMap;
  stringMap["abc"] = 1;
  stringMap["def"] = 10;
  stringMap["ghi"] = 3;
  unique_ptr<GEDocument> doc = CreateEmptyDocument("test");
  DOMElement * elem = doc->getDocumentElement();
  AddElement(elem, "data", stringMap);

  DOMNodeList * dataElem = elem->getElementsByTagName(ToXMLStr("data"));
  ASSERT_EQ(dataElem->getLength(), 1) << "Wrong number of data elements extracted from XML";
  ASSERT_EQ(FromXMLStr(dataElem->item(0)->getNodeName()), "data") << "Unexpected tag name when extracting data element";
  DOMNode * child = dataElem->item(0)->getFirstChild();
  ASSERT_EQ(FromXMLStr(child->getNodeName()), "item") << "Invalid parent element when writing map of strings";
  ASSERT_EQ(getAttribute(child, "key"), "abc") << "Invalid key when writing map of strings";
  ASSERT_EQ(getValue(child), 1) << "Invalid value when writing map of strings";
  child = child->getNextSibling();
  ASSERT_EQ(FromXMLStr(child->getNodeName()), "item") << "Invalid parent element when writing map of strings";
  ASSERT_EQ(getAttribute(child, "key"), "def") << "Invalid key when writing map of strings";
  ASSERT_EQ(getValue(child), 10) << "Invalid value when writing map of strings";
  child = child->getNextSibling();
  ASSERT_EQ(FromXMLStr(child->getNodeName()), "item") << "Invalid parent element when writing map of strings";
  ASSERT_EQ(getAttribute(child, "key"), "ghi") << "Invalid key when writing map of strings";
  ASSERT_EQ(getValue(child), 3) << "Invalid value when writing map of strings";
  child = child->getNextSibling();
  ASSERT_EQ(child, nullptr) << "Unexpected element";
}

TEST_F(KhxmlTest, StdStringMapToElement) {
  fromStringMapTest<string>();
}

TEST_F(KhxmlTest, SharedStringMapToElement) {
  fromStringMapTest<SharedString>();
}

TEST_F(KhxmlTest, QStringMapToElement) {
  fromStringMapTest<QString>();
}

template <class String>
void toStringTest() {
  string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<test>expected_value</test>";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(xml, "dummy");
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(FromXMLStr(elem->getTagName()), "test") << "Invalid tag name when reading string from XML";
  String value;
  FromElement(elem, value);
  ASSERT_EQ(value, "expected_value") << "Could not read string value from XML correctly";
}

TEST_F(KhxmlTest, StdStringReadString) {
  toStringTest<string>();
}

TEST_F(KhxmlTest, SharedStringReadString) {
  toStringTest<SharedString>();
}

TEST_F(KhxmlTest, QStringReadString) {
  toStringTest<QString>();
}

template <class String>
void runXmlToStringMapTest(const std::string & xml) {
  unique_ptr<GEDocument> doc = ReadDocumentFromString(xml, "dummy");
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(FromXMLStr(elem->getTagName()), "data") << "Invalid tag name when reading string from XML";
  map<String, int> results;
  FromElement(elem, results);
  ASSERT_EQ(results["xyz"], 42) << "Error reading map of strings from XML";
  ASSERT_EQ(results["wvu"], 12) << "Error reading map of strings from XML";
}

template <class String>
void toStringMapTest() {
  // There are two methods of parsing maps from XML. We test both.
  string newxml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<data>\n"
         "<item key=\"xyz\">42</item>\n"
         "<item key=\"wvu\">12</item>\n"
      "</data>\n";
  runXmlToStringMapTest<String>(newxml);
  string oldxml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<data>\n"
        "<xyz>42</xyz>\n"
        "<wvu>12</wvu>\n"
      "</data>\n";
  runXmlToStringMapTest<String>(oldxml);
}

TEST_F(KhxmlTest, StdStringReadMap) {
  toStringMapTest<string>();
}

TEST_F(KhxmlTest, SharedStringReadMap) {
  toStringMapTest<SharedString>();
}

TEST_F(KhxmlTest, QStringReadMap) {
  toStringMapTest<QString>();
}

template <class String>
void readEmptyStringFromXml() {
  string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<value></value>\n";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(xml, "dummy");
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(FromXMLStr(elem->getTagName()), "value") << "Invalid tag name when reading empty string from XML";
  String val;
  FromElement(elem, val);
  ASSERT_EQ(val, String("")) << "Error reading empty string from XML";
}

TEST_F(KhxmlTest, StdStringReadEmpty) {
  readEmptyStringFromXml<string>();
}

TEST_F(KhxmlTest, SharedStringReadEmpty) {
  readEmptyStringFromXml<SharedString>();
}

TEST_F(KhxmlTest, QStringReadEmpty) {
  readEmptyStringFromXml<QString>();
}

template <class String>
void readNonEmptyStringFromXml() {
  string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
      "<value>qwertyuiop</value>\n";
  unique_ptr<GEDocument> doc = ReadDocumentFromString(xml, "dummy");
  DOMElement * elem = doc->getDocumentElement();
  ASSERT_EQ(FromXMLStr(elem->getTagName()), "value") << "Invalid tag name when reading empty string from XML";
  String val;
  FromElement(elem, val);
  ASSERT_EQ(val, String("qwertyuiop")) << "Error reading empty string from XML";
}

TEST_F(KhxmlTest, StdStringReadNonEmpty) {
  readNonEmptyStringFromXml<string>();
}

TEST_F(KhxmlTest, SharedStringReadNonEmpty) {
  readNonEmptyStringFromXml<SharedString>();
}

TEST_F(KhxmlTest, QStringReadNonEmpty) {
  readNonEmptyStringFromXml<QString>();
}

int main(int argc, char** argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before><commit_msg>Add NOTREACHED for someone trying to change the title on the bookmark bar folder or other bookmarks folders.<commit_after><|endoftext|>