<commit_before>/*************************************************************************/
/*  audio_player.cpp                                                     */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "audio_player.h"

#include "engine.h"

void AudioStreamPlayer::_mix_internal(bool p_fadeout) {

	int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);

	//get data
	AudioFrame *buffer = mix_buffer.ptrw();
	int buffer_size = mix_buffer.size();

	if (p_fadeout) {
		buffer_size = MIN(buffer_size, 16); //short fadeout ramp
	}

	//mix
	stream_playback->mix(buffer, 1.0, buffer_size);

	//multiply volume interpolating to avoid clicks if this changes
	float target_volume = p_fadeout ? -80.0 : volume_db;
	float vol = Math::db2linear(mix_volume_db);
	float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);

	for (int i = 0; i < buffer_size; i++) {
		buffer[i] *= vol;
		vol += vol_inc;
	}
	//set volume for next mix
	mix_volume_db = target_volume;

	AudioFrame *targets[4] = { NULL, NULL, NULL, NULL };

	if (AudioServer::get_singleton()->get_speaker_mode() == AudioServer::SPEAKER_MODE_STEREO) {
		targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0);
	} else {
		switch (mix_target) {
			case MIX_TARGET_STEREO: {
				targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0);
			} break;
			case MIX_TARGET_SURROUND: {
				for (int i = 0; i < AudioServer::get_singleton()->get_channel_count(); i++) {
					targets[i] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, i);
				}
			} break;
			case MIX_TARGET_CENTER: {
				targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1);
			} break;
		}
	}

	for (int c = 0; c < 4; c++) {
		if (!targets[c])
			break;
		for (int i = 0; i < buffer_size; i++) {
			targets[c][i] += buffer[i];
		}
	}
}

void AudioStreamPlayer::_mix_audio() {

	if (!stream_playback.is_valid()) {
		return;
	}

	if (!active) {
		return;
	}

	if (setseek >= 0.0) {
		if (stream_playback->is_playing()) {

			//fade out to avoid pops
			_mix_internal(true);
		}
		stream_playback->start(setseek);
		setseek = -1.0; //reset seek
		mix_volume_db = volume_db; //reset ramp
	}

	_mix_internal(false);
}

void AudioStreamPlayer::_notification(int p_what) {

	if (p_what == NOTIFICATION_ENTER_TREE) {

		AudioServer::get_singleton()->add_callback(_mix_audios, this);
		if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
			play();
		}
	}

	if (p_what == NOTIFICATION_INTERNAL_PROCESS) {

		if (!active || (setseek < 0 && !stream_playback->is_playing())) {
			active = false;
			emit_signal("finished");
			set_process_internal(false);
		}
	}

	if (p_what == NOTIFICATION_EXIT_TREE) {

		AudioServer::get_singleton()->remove_callback(_mix_audios, this);
	}
}

void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {

	AudioServer::get_singleton()->lock();

	mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());

	if (stream_playback.is_valid()) {
		stream_playback.unref();
		stream.unref();
		active = false;
		setseek = -1;
	}

	if (p_stream.is_valid()) {
		stream = p_stream;
		stream_playback = p_stream->instance_playback();
	}

	AudioServer::get_singleton()->unlock();

	if (p_stream.is_valid() && stream_playback.is_null()) {
		stream.unref();
		ERR_FAIL_COND(stream_playback.is_null());
	}
}

Ref<AudioStream> AudioStreamPlayer::get_stream() const {

	return stream;
}

void AudioStreamPlayer::set_volume_db(float p_volume) {

	volume_db = p_volume;
}
float AudioStreamPlayer::get_volume_db() const {

	return volume_db;
}

void AudioStreamPlayer::play(float p_from_pos) {

	if (stream_playback.is_valid()) {
		//mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks
		setseek = p_from_pos;
		active = true;
		set_process_internal(true);
	}
}

void AudioStreamPlayer::seek(float p_seconds) {

	if (stream_playback.is_valid()) {
		setseek = p_seconds;
	}
}

void AudioStreamPlayer::stop() {

	if (stream_playback.is_valid()) {
		active = false;
		set_process_internal(false);
	}
}

bool AudioStreamPlayer::is_playing() const {

	if (stream_playback.is_valid()) {
		return active; //&& stream_playback->is_playing();
	}

	return false;
}

float AudioStreamPlayer::get_playback_position() {

	if (stream_playback.is_valid()) {
		return stream_playback->get_playback_position();
	}

	return 0;
}

void AudioStreamPlayer::set_bus(const StringName &p_bus) {

	//if audio is active, must lock this
	AudioServer::get_singleton()->lock();
	bus = p_bus;
	AudioServer::get_singleton()->unlock();
}
StringName AudioStreamPlayer::get_bus() const {

	for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
		if (AudioServer::get_singleton()->get_bus_name(i) == bus) {
			return bus;
		}
	}
	return "Master";
}

void AudioStreamPlayer::set_autoplay(bool p_enable) {

	autoplay = p_enable;
}
bool AudioStreamPlayer::is_autoplay_enabled() {

	return autoplay;
}

void AudioStreamPlayer::set_mix_target(MixTarget p_target) {

	mix_target = p_target;
}

AudioStreamPlayer::MixTarget AudioStreamPlayer::get_mix_target() const {

	return mix_target;
}

void AudioStreamPlayer::_set_playing(bool p_enable) {

	if (p_enable)
		play();
	else
		stop();
}
bool AudioStreamPlayer::_is_active() const {

	return active;
}

void AudioStreamPlayer::_validate_property(PropertyInfo &property) const {

	if (property.name == "bus") {

		String options;
		for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
			if (i > 0)
				options += ",";
			String name = AudioServer::get_singleton()->get_bus_name(i);
			options += name;
		}

		property.hint_string = options;
	}
}

void AudioStreamPlayer::_bus_layout_changed() {

	_change_notify();
}

void AudioStreamPlayer::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer::set_stream);
	ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer::get_stream);

	ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer::set_volume_db);
	ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer::get_volume_db);

	ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer::play, DEFVAL(0.0));
	ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer::seek);
	ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer::stop);

	ClassDB::bind_method(D_METHOD("is_playing"), &AudioStreamPlayer::is_playing);
	ClassDB::bind_method(D_METHOD("get_playback_position"), &AudioStreamPlayer::get_playback_position);

	ClassDB::bind_method(D_METHOD("set_bus", "bus"), &AudioStreamPlayer::set_bus);
	ClassDB::bind_method(D_METHOD("get_bus"), &AudioStreamPlayer::get_bus);

	ClassDB::bind_method(D_METHOD("set_autoplay", "enable"), &AudioStreamPlayer::set_autoplay);
	ClassDB::bind_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer::is_autoplay_enabled);

	ClassDB::bind_method(D_METHOD("set_mix_target", "mix_target"), &AudioStreamPlayer::set_mix_target);
	ClassDB::bind_method(D_METHOD("get_mix_target"), &AudioStreamPlayer::get_mix_target);

	ClassDB::bind_method(D_METHOD("_set_playing", "enable"), &AudioStreamPlayer::_set_playing);
	ClassDB::bind_method(D_METHOD("_is_active"), &AudioStreamPlayer::_is_active);

	ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer::_bus_layout_changed);

	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
	ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
	ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target");
	ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");

	ADD_SIGNAL(MethodInfo("finished"));

	BIND_ENUM_CONSTANT(MIX_TARGET_STEREO);
	BIND_ENUM_CONSTANT(MIX_TARGET_SURROUND);
	BIND_ENUM_CONSTANT(MIX_TARGET_CENTER);
}

AudioStreamPlayer::AudioStreamPlayer() {

	mix_volume_db = 0;
	volume_db = 0;
	autoplay = false;
	setseek = -1;
	active = false;
	mix_target = MIX_TARGET_STEREO;

	AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
}

AudioStreamPlayer::~AudioStreamPlayer() {
}
<commit_msg>Fix issue 15895, audio streams don't signalling finished after the first one if the audio player is set to play again due to the order of calls in _notification. First it emits the signal, and later it disable the internal processing regardless what the callback did.<commit_after>/*************************************************************************/
/*  audio_player.cpp                                                     */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "audio_player.h"

#include "engine.h"

void AudioStreamPlayer::_mix_internal(bool p_fadeout) {

	int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);

	//get data
	AudioFrame *buffer = mix_buffer.ptrw();
	int buffer_size = mix_buffer.size();

	if (p_fadeout) {
		buffer_size = MIN(buffer_size, 16); //short fadeout ramp
	}

	//mix
	stream_playback->mix(buffer, 1.0, buffer_size);

	//multiply volume interpolating to avoid clicks if this changes
	float target_volume = p_fadeout ? -80.0 : volume_db;
	float vol = Math::db2linear(mix_volume_db);
	float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);

	for (int i = 0; i < buffer_size; i++) {
		buffer[i] *= vol;
		vol += vol_inc;
	}
	//set volume for next mix
	mix_volume_db = target_volume;

	AudioFrame *targets[4] = { NULL, NULL, NULL, NULL };

	if (AudioServer::get_singleton()->get_speaker_mode() == AudioServer::SPEAKER_MODE_STEREO) {
		targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0);
	} else {
		switch (mix_target) {
			case MIX_TARGET_STEREO: {
				targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0);
			} break;
			case MIX_TARGET_SURROUND: {
				for (int i = 0; i < AudioServer::get_singleton()->get_channel_count(); i++) {
					targets[i] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, i);
				}
			} break;
			case MIX_TARGET_CENTER: {
				targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1);
			} break;
		}
	}

	for (int c = 0; c < 4; c++) {
		if (!targets[c])
			break;
		for (int i = 0; i < buffer_size; i++) {
			targets[c][i] += buffer[i];
		}
	}
}

void AudioStreamPlayer::_mix_audio() {

	if (!stream_playback.is_valid()) {
		return;
	}

	if (!active) {
		return;
	}

	if (setseek >= 0.0) {
		if (stream_playback->is_playing()) {

			//fade out to avoid pops
			_mix_internal(true);
		}
		stream_playback->start(setseek);
		setseek = -1.0; //reset seek
		mix_volume_db = volume_db; //reset ramp
	}

	_mix_internal(false);
}

void AudioStreamPlayer::_notification(int p_what) {

	if (p_what == NOTIFICATION_ENTER_TREE) {

		AudioServer::get_singleton()->add_callback(_mix_audios, this);
		if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
			play();
		}
	}

	if (p_what == NOTIFICATION_INTERNAL_PROCESS) {

		if (!active || (setseek < 0 && !stream_playback->is_playing())) {
			active = false;
			set_process_internal(false);
			emit_signal("finished");
		}
	}

	if (p_what == NOTIFICATION_EXIT_TREE) {

		AudioServer::get_singleton()->remove_callback(_mix_audios, this);
	}
}

void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {

	AudioServer::get_singleton()->lock();

	mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());

	if (stream_playback.is_valid()) {
		stream_playback.unref();
		stream.unref();
		active = false;
		setseek = -1;
	}

	if (p_stream.is_valid()) {
		stream = p_stream;
		stream_playback = p_stream->instance_playback();
	}

	AudioServer::get_singleton()->unlock();

	if (p_stream.is_valid() && stream_playback.is_null()) {
		stream.unref();
		ERR_FAIL_COND(stream_playback.is_null());
	}
}

Ref<AudioStream> AudioStreamPlayer::get_stream() const {

	return stream;
}

void AudioStreamPlayer::set_volume_db(float p_volume) {

	volume_db = p_volume;
}
float AudioStreamPlayer::get_volume_db() const {

	return volume_db;
}

void AudioStreamPlayer::play(float p_from_pos) {

	if (stream_playback.is_valid()) {
		//mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks
		setseek = p_from_pos;
		active = true;
		set_process_internal(true);
	}
}

void AudioStreamPlayer::seek(float p_seconds) {

	if (stream_playback.is_valid()) {
		setseek = p_seconds;
	}
}

void AudioStreamPlayer::stop() {

	if (stream_playback.is_valid()) {
		active = false;
		set_process_internal(false);
	}
}

bool AudioStreamPlayer::is_playing() const {

	if (stream_playback.is_valid()) {
		return active; //&& stream_playback->is_playing();
	}

	return false;
}

float AudioStreamPlayer::get_playback_position() {

	if (stream_playback.is_valid()) {
		return stream_playback->get_playback_position();
	}

	return 0;
}

void AudioStreamPlayer::set_bus(const StringName &p_bus) {

	//if audio is active, must lock this
	AudioServer::get_singleton()->lock();
	bus = p_bus;
	AudioServer::get_singleton()->unlock();
}
StringName AudioStreamPlayer::get_bus() const {

	for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
		if (AudioServer::get_singleton()->get_bus_name(i) == bus) {
			return bus;
		}
	}
	return "Master";
}

void AudioStreamPlayer::set_autoplay(bool p_enable) {

	autoplay = p_enable;
}
bool AudioStreamPlayer::is_autoplay_enabled() {

	return autoplay;
}

void AudioStreamPlayer::set_mix_target(MixTarget p_target) {

	mix_target = p_target;
}

AudioStreamPlayer::MixTarget AudioStreamPlayer::get_mix_target() const {

	return mix_target;
}

void AudioStreamPlayer::_set_playing(bool p_enable) {

	if (p_enable)
		play();
	else
		stop();
}
bool AudioStreamPlayer::_is_active() const {

	return active;
}

void AudioStreamPlayer::_validate_property(PropertyInfo &property) const {

	if (property.name == "bus") {

		String options;
		for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
			if (i > 0)
				options += ",";
			String name = AudioServer::get_singleton()->get_bus_name(i);
			options += name;
		}

		property.hint_string = options;
	}
}

void AudioStreamPlayer::_bus_layout_changed() {

	_change_notify();
}

void AudioStreamPlayer::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer::set_stream);
	ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer::get_stream);

	ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer::set_volume_db);
	ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer::get_volume_db);

	ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer::play, DEFVAL(0.0));
	ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer::seek);
	ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer::stop);

	ClassDB::bind_method(D_METHOD("is_playing"), &AudioStreamPlayer::is_playing);
	ClassDB::bind_method(D_METHOD("get_playback_position"), &AudioStreamPlayer::get_playback_position);

	ClassDB::bind_method(D_METHOD("set_bus", "bus"), &AudioStreamPlayer::set_bus);
	ClassDB::bind_method(D_METHOD("get_bus"), &AudioStreamPlayer::get_bus);

	ClassDB::bind_method(D_METHOD("set_autoplay", "enable"), &AudioStreamPlayer::set_autoplay);
	ClassDB::bind_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer::is_autoplay_enabled);

	ClassDB::bind_method(D_METHOD("set_mix_target", "mix_target"), &AudioStreamPlayer::set_mix_target);
	ClassDB::bind_method(D_METHOD("get_mix_target"), &AudioStreamPlayer::get_mix_target);

	ClassDB::bind_method(D_METHOD("_set_playing", "enable"), &AudioStreamPlayer::_set_playing);
	ClassDB::bind_method(D_METHOD("_is_active"), &AudioStreamPlayer::_is_active);

	ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer::_bus_layout_changed);

	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
	ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
	ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target");
	ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");

	ADD_SIGNAL(MethodInfo("finished"));

	BIND_ENUM_CONSTANT(MIX_TARGET_STEREO);
	BIND_ENUM_CONSTANT(MIX_TARGET_SURROUND);
	BIND_ENUM_CONSTANT(MIX_TARGET_CENTER);
}

AudioStreamPlayer::AudioStreamPlayer() {

	mix_volume_db = 0;
	volume_db = 0;
	autoplay = false;
	setseek = -1;
	active = false;
	mix_target = MIX_TARGET_STEREO;

	AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
}

AudioStreamPlayer::~AudioStreamPlayer() {
}
<|endoftext|>


<commit_before>/*************************************************************************/
/*  grid_container.cpp                                                   */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur.                 */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "grid_container.h"

void GridContainer::_notification(int p_what) {

	switch(p_what) {

		case NOTIFICATION_SORT_CHILDREN: {

			Map<int,int> col_minw;
			Map<int,int> row_minh;
			Set<int> col_expanded;
			Set<int> row_expanded;

			int sep=get_constant("separation");

			int idx=0;
			int max_row=0;
			int max_col=0;

			Size2 size = get_size();

			for(int i=0;i<get_child_count();i++) {

				Control *c = get_child(i)->cast_to<Control>();
				if (!c || !c->is_visible())
					continue;

				int row = idx / columns;
				int col = idx % columns;

				Size2i ms = c->get_combined_minimum_size();
				if (col_minw.has(col))
					col_minw[col] = MAX(col_minw[col],ms.width);
				else
					col_minw[col]=ms.width;

				if (row_minh.has(row))
					row_minh[row] = MAX(row_minh[row],ms.height);
				else
					row_minh[row]=ms.height;

				if (c->get_h_size_flags()&SIZE_EXPAND)
					col_expanded.insert(col);
				if (c->get_v_size_flags()&SIZE_EXPAND)
					row_expanded.insert(row);

				max_col=MAX(col,max_col);
				max_row=MAX(row,max_row);
				idx++;
			}

			Size2 ms;
			int expand_rows=0;
			int expand_cols=0;

			for (Map<int,int>::Element *E=col_minw.front();E;E=E->next()) {
				ms.width+=E->get();
				if (col_expanded.has(E->key()))
					expand_cols++;
			}

			for (Map<int,int>::Element *E=row_minh.front();E;E=E->next()) {
				ms.height+=E->get();
				if (row_expanded.has(E->key()))
					expand_rows++;
			}

			ms.height+=sep*max_row;
			ms.width+=sep*max_col;

			int row_expand = expand_rows?(size.y-ms.y)/expand_rows:0;
			int col_expand = expand_cols?(size.x-ms.x)/expand_cols:0;

			int col_ofs=0;
			int row_ofs=0;
			idx=0;

			for(int i=0;i<get_child_count();i++) {

				Control *c = get_child(i)->cast_to<Control>();
				if (!c || !c->is_visible())
					continue;
				int row = idx / columns;
				int col = idx % columns;

				if (col==0) {
					col_ofs=0;
					if (row>0 && row_minh.has(row-1))
						row_ofs+=row_minh[row-1]+sep+(row_expanded.has(row-1)?row_expand:0);
				}

				if (c->is_visible()) {
					Size2 s;
					if (col_minw.has(col))
						s.width=col_minw[col];
					if (row_minh.has(row))
						s.height=row_minh[col];

					if (row_expanded.has(row))
						s.height+=row_expand;
					if (col_expanded.has(col))
						s.width+=col_expand;

					Point2 p(col_ofs,row_ofs);

					fit_child_in_rect(c,Rect2(p,s));

				}

				if (col_minw.has(col)) {
					col_ofs+=col_minw[col]+sep+(col_expanded.has(col)?col_expand:0);
				}

				idx++;
			}

		} break;
	}

}

void GridContainer::set_columns(int p_columns) {

	columns=p_columns;
	queue_sort();

}

int GridContainer::get_columns() const{

	return columns;
}

void GridContainer::_bind_methods(){

	ObjectTypeDB::bind_method(_MD("set_columns","columns"),&GridContainer::set_columns);
	ObjectTypeDB::bind_method(_MD("get_columns"),&GridContainer::get_columns);

	ADD_PROPERTY( PropertyInfo(Variant::INT,"columns",PROPERTY_HINT_RANGE,"1,1024,1"),_SCS("set_columns"),_SCS("get_columns"));
}

Size2 GridContainer::get_minimum_size() const {

	Map<int,int> col_minw;
	Map<int,int> row_minh;

	int sep=get_constant("separation");

	int idx=0;
	int max_row=0;
	int max_col=0;

	for(int i=0;i<get_child_count();i++) {

		Control *c = get_child(i)->cast_to<Control>();
		if (!c || !c->is_visible())
			continue;
		int row = idx / columns;
		int col = idx % columns;
		Size2i ms = c->get_combined_minimum_size();
		if (col_minw.has(col))
			col_minw[col] = MAX(col_minw[col],ms.width);
		else
			col_minw[col]=ms.width;

		if (row_minh.has(row))
			row_minh[row] = MAX(row_minh[row],ms.height);
		else
			row_minh[row]=ms.height;
		max_col=MAX(col,max_col);
		max_row=MAX(row,max_row);
		idx++;
	}

	Size2 ms;

	for (Map<int,int>::Element *E=col_minw.front();E;E=E->next()) {
		ms.width+=E->get();
	}

	for (Map<int,int>::Element *E=row_minh.front();E;E=E->next()) {
		ms.height+=E->get();
	}

	ms.height+=sep*max_row;
	ms.width+=sep*max_col;

	return ms;

}

GridContainer::GridContainer() {

	set_stop_mouse(false);
	columns=1;
}
<commit_msg>bad lookup in grid container corrected, cells are properly aligned again. Fixes #1462<commit_after>/*************************************************************************/
/*  grid_container.cpp                                                   */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur.                 */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "grid_container.h"

void GridContainer::_notification(int p_what) {

	switch(p_what) {

		case NOTIFICATION_SORT_CHILDREN: {

			Map<int,int> col_minw;
			Map<int,int> row_minh;
			Set<int> col_expanded;
			Set<int> row_expanded;

			int sep=get_constant("separation");

			int idx=0;
			int max_row=0;
			int max_col=0;

			Size2 size = get_size();

			for(int i=0;i<get_child_count();i++) {

				Control *c = get_child(i)->cast_to<Control>();
				if (!c || !c->is_visible())
					continue;

				int row = idx / columns;
				int col = idx % columns;

				Size2i ms = c->get_combined_minimum_size();
				if (col_minw.has(col))
					col_minw[col] = MAX(col_minw[col],ms.width);
				else
					col_minw[col]=ms.width;

				if (row_minh.has(row))
					row_minh[row] = MAX(row_minh[row],ms.height);
				else
					row_minh[row]=ms.height;

			//	print_line("store row "+itos(row)+" mw "+itos(ms.height));

				if (c->get_h_size_flags()&SIZE_EXPAND)
					col_expanded.insert(col);
				if (c->get_v_size_flags()&SIZE_EXPAND)
					row_expanded.insert(row);

				max_col=MAX(col,max_col);
				max_row=MAX(row,max_row);
				idx++;
			}

			Size2 ms;
			int expand_rows=0;
			int expand_cols=0;

			for (Map<int,int>::Element *E=col_minw.front();E;E=E->next()) {
				ms.width+=E->get();
				if (col_expanded.has(E->key()))
					expand_cols++;
			}

			for (Map<int,int>::Element *E=row_minh.front();E;E=E->next()) {
				ms.height+=E->get();
				if (row_expanded.has(E->key()))
					expand_rows++;
			}

			ms.height+=sep*max_row;
			ms.width+=sep*max_col;

			int row_expand = expand_rows?(size.y-ms.y)/expand_rows:0;
			int col_expand = expand_cols?(size.x-ms.x)/expand_cols:0;

			int col_ofs=0;
			int row_ofs=0;
			idx=0;

			for(int i=0;i<get_child_count();i++) {

				Control *c = get_child(i)->cast_to<Control>();
				if (!c || !c->is_visible())
					continue;
				int row = idx / columns;
				int col = idx % columns;

				if (col==0) {
					col_ofs=0;
					if (row>0 && row_minh.has(row-1))
						row_ofs+=row_minh[row-1]+sep+(row_expanded.has(row-1)?row_expand:0);
				}

				Size2 s;
				if (col_minw.has(col))
					s.width=col_minw[col];
				if (row_minh.has(row))
					s.height=row_minh[row];

				if (row_expanded.has(row))
					s.height+=row_expand;
				if (col_expanded.has(col))
					s.width+=col_expand;

				Point2 p(col_ofs,row_ofs);

//				print_line("col: "+itos(col)+" row: "+itos(row)+" col_ofs: "+itos(col_ofs)+" row_ofs: "+itos(row_ofs));
				fit_child_in_rect(c,Rect2(p,s));
				//print_line("col: "+itos(col)+" row: "+itos(row)+" rect: "+Rect2(p,s));

				if (col_minw.has(col)) {
					col_ofs+=col_minw[col]+sep+(col_expanded.has(col)?col_expand:0);
				}

				idx++;
			}

		} break;
	}

}

void GridContainer::set_columns(int p_columns) {

	columns=p_columns;
	queue_sort();

}

int GridContainer::get_columns() const{

	return columns;
}

void GridContainer::_bind_methods(){

	ObjectTypeDB::bind_method(_MD("set_columns","columns"),&GridContainer::set_columns);
	ObjectTypeDB::bind_method(_MD("get_columns"),&GridContainer::get_columns);

	ADD_PROPERTY( PropertyInfo(Variant::INT,"columns",PROPERTY_HINT_RANGE,"1,1024,1"),_SCS("set_columns"),_SCS("get_columns"));
}

Size2 GridContainer::get_minimum_size() const {

	Map<int,int> col_minw;
	Map<int,int> row_minh;

	int sep=get_constant("separation");

	int idx=0;
	int max_row=0;
	int max_col=0;

	for(int i=0;i<get_child_count();i++) {

		Control *c = get_child(i)->cast_to<Control>();
		if (!c || !c->is_visible())
			continue;
		int row = idx / columns;
		int col = idx % columns;
		Size2i ms = c->get_combined_minimum_size();
		if (col_minw.has(col))
			col_minw[col] = MAX(col_minw[col],ms.width);
		else
			col_minw[col]=ms.width;

		if (row_minh.has(row))
			row_minh[row] = MAX(row_minh[row],ms.height);
		else
			row_minh[row]=ms.height;
		max_col=MAX(col,max_col);
		max_row=MAX(row,max_row);
		idx++;
	}

	Size2 ms;

	for (Map<int,int>::Element *E=col_minw.front();E;E=E->next()) {
		ms.width+=E->get();
	}

	for (Map<int,int>::Element *E=row_minh.front();E;E=E->next()) {
		ms.height+=E->get();
	}

	ms.height+=sep*max_row;
	ms.width+=sep*max_col;

	return ms;

}

GridContainer::GridContainer() {

	set_stop_mouse(false);
	columns=1;
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "Environment.h"
#include <regex>
#include <set>
#include "Handle.h"
#include "ErrorUtilities.h"
#include "StringUtilities.h"

static const wregex EnvVarRegex = wregex(L"(.+)=(.*)");

// In upper case
static const set<wstring> AutoOverrides = {
	L"APPDATA",
	L"HOMEPATH",
	L"LOCALAPPDATA",
	L"USERDOMAIN",
	L"USERDOMAIN_ROAMINGPROFILE",
	L"USERNAME",
	L"USERPROFILE"
};

Result<Environment> Environment::CreateForCurrentProcess()
{
	auto environment = GetEnvironmentStringsW();
	Environment newEnvironment;
	try
	{
		newEnvironment.CreateVariableMap(environment);
	}
	catch(...)
	{
	}

	FreeEnvironmentStringsW(environment);
	return newEnvironment;
}

Result<Environment> Environment::CreateForUser(Handle& token, bool inherit)
{
	Environment newEnvironment;
	LPVOID environment;
	if (!CreateEnvironmentBlock(&environment, token, inherit))
	{
		return Result<Environment>(ErrorUtilities::GetErrorCode(), ErrorUtilities::GetLastErrorMessage(L"CreateEnvironmentBlock"));
	}

	try
	{
		newEnvironment.CreateVariableMap(environment);
	}
	catch (...)
	{
	}

	DestroyEnvironmentBlock(environment);
	return newEnvironment;
}

Environment Environment::CreateFormString(wstring variables)
{
	auto vars = StringUtilities::Split(variables, L"\n");

	Environment environment;
	wsmatch matchResult;
	for (auto varsIterrator = vars.begin(); varsIterrator != vars.end(); ++varsIterrator)
	{
		if (!regex_search(*varsIterrator, matchResult, EnvVarRegex))
		{
			continue;
		}

		auto envName = matchResult._At(1).str();
		auto envValue = matchResult._At(2).str();
		environment._vars[envName] = envValue;
		environment._empty = false;
	}

	return environment;
}

Environment Environment::Override(Environment& destinarionEnvironment, Environment& sourceEnvironment)
{
	Environment targetEnvironment;
	for (auto varsIterator = destinarionEnvironment._vars.begin(); varsIterator != destinarionEnvironment._vars.end(); ++varsIterator)
	{
		targetEnvironment._vars[varsIterator->first] = varsIterator->second;
		targetEnvironment._empty = false;
	}

	auto autoOverrides = GetAutoOverrides();
	for (auto varsIterator = sourceEnvironment._vars.begin(); varsIterator != sourceEnvironment._vars.end(); ++varsIterator)
	{
		auto varNameInLowCase = StringUtilities::Convert(varsIterator->first, toupper);
		if (autoOverrides.find(varNameInLowCase) != autoOverrides.end())
		{
			targetEnvironment._vars[varsIterator->first] = varsIterator->second;
		}
	}

	return targetEnvironment;
}

Environment::~Environment()
{
	for (auto environmentBlockIterator = _environmentBlocks.begin(); environmentBlockIterator != _environmentBlocks.end(); ++environmentBlockIterator)
	{
		delete *environmentBlockIterator;
	}

	_environmentBlocks.clear();
}

void Environment::CreateVariableMap(LPVOID environment)
{
	_vars.clear();

	auto curVar = static_cast<LPCWSTR>(environment);
	size_t len;
	do
	{
		wstring curVarValue(curVar);
		len = curVarValue.size();
		if (len == 0)
		{
			continue;
		}

		curVar += len + 1;
		wsmatch matchResult;
		if (!regex_search(curVarValue, matchResult, EnvVarRegex))
		{
			continue;
		}

		auto envName = matchResult._At(1).str();
		auto envValue = matchResult._At(2).str();
		_vars[envName] = envValue;
		_empty = false;
	} while (len > 0);
}

LPVOID* Environment::CreateEnvironmentFromMap()
{
	size_t size = 0;
	for (auto varsIterator = _vars.begin(); varsIterator != _vars.end(); ++varsIterator)
	{
		size += varsIterator->first.size() + varsIterator->second.size() + 2;
	}

	size++;

	auto environment = new _TCHAR[size];
	memset(environment, 0, sizeof(_TCHAR) * size);

	size_t pointer = 0;
	for (auto varsIterator = _vars.begin(); varsIterator != _vars.end(); ++varsIterator)
	{
		auto curSize = varsIterator->first.size();
		memcpy(environment + pointer, varsIterator->first.c_str(), curSize * sizeof(_TCHAR));
		pointer += curSize;

		environment[pointer] = _TCHAR('=');
		pointer ++;

		curSize = varsIterator->second.size();
		memcpy(environment + pointer, varsIterator->second.c_str(), curSize * sizeof(_TCHAR));
		pointer += curSize;

		pointer ++;
	}

	return reinterpret_cast<LPVOID*>(environment);
}

LPVOID* Environment::CreateEnvironment()
{
	if(_empty)
	{
		return nullptr;
	}

	auto environment = CreateEnvironmentFromMap();
	_environmentBlocks.push_back(environment);
	return environment;
}

wstring Environment::TryGetValue(wstring variableName)
{
	auto curVarNameInLowCase = StringUtilities::Convert(variableName, tolower);
	for (auto varsIterator = _vars.begin(); varsIterator != _vars.end(); ++varsIterator)
	{
		auto varNameInLowCase = StringUtilities::Convert(varsIterator->first, tolower);
		if (varNameInLowCase == curVarNameInLowCase)
		{
			return varsIterator->second;
		}
	}

	return L"";
}

set<wstring> Environment::GetAutoOverrides()
{
	return AutoOverrides;
}<commit_msg>Add HOMEDRIVE env var to the list of overrides<commit_after>#include "stdafx.h"
#include "Environment.h"
#include <regex>
#include <set>
#include "Handle.h"
#include "ErrorUtilities.h"
#include "StringUtilities.h"

static const wregex EnvVarRegex = wregex(L"(.+)=(.*)");

// In upper case
static const set<wstring> AutoOverrides = {
	L"APPDATA",
	L"HOMEPATH",
	L"HOMEDRIVE",
	L"LOCALAPPDATA",
	L"USERDOMAIN",
	L"USERDOMAIN_ROAMINGPROFILE",
	L"USERNAME",
	L"USERPROFILE"
};

Result<Environment> Environment::CreateForCurrentProcess()
{
	auto environment = GetEnvironmentStringsW();
	Environment newEnvironment;
	try
	{
		newEnvironment.CreateVariableMap(environment);
	}
	catch(...)
	{
	}

	FreeEnvironmentStringsW(environment);
	return newEnvironment;
}

Result<Environment> Environment::CreateForUser(Handle& token, bool inherit)
{
	Environment newEnvironment;
	LPVOID environment;
	if (!CreateEnvironmentBlock(&environment, token, inherit))
	{
		return Result<Environment>(ErrorUtilities::GetErrorCode(), ErrorUtilities::GetLastErrorMessage(L"CreateEnvironmentBlock"));
	}

	try
	{
		newEnvironment.CreateVariableMap(environment);
	}
	catch (...)
	{
	}

	DestroyEnvironmentBlock(environment);
	return newEnvironment;
}

Environment Environment::CreateFormString(wstring variables)
{
	auto vars = StringUtilities::Split(variables, L"\n");

	Environment environment;
	wsmatch matchResult;
	for (auto varsIterrator = vars.begin(); varsIterrator != vars.end(); ++varsIterrator)
	{
		if (!regex_search(*varsIterrator, matchResult, EnvVarRegex))
		{
			continue;
		}

		auto envName = matchResult._At(1).str();
		auto envValue = matchResult._At(2).str();
		environment._vars[envName] = envValue;
		environment._empty = false;
	}

	return environment;
}

Environment Environment::Override(Environment& destinarionEnvironment, Environment& sourceEnvironment)
{
	Environment targetEnvironment;
	for (auto varsIterator = destinarionEnvironment._vars.begin(); varsIterator != destinarionEnvironment._vars.end(); ++varsIterator)
	{
		targetEnvironment._vars[varsIterator->first] = varsIterator->second;
		targetEnvironment._empty = false;
	}

	auto autoOverrides = GetAutoOverrides();
	for (auto varsIterator = sourceEnvironment._vars.begin(); varsIterator != sourceEnvironment._vars.end(); ++varsIterator)
	{
		auto varNameInLowCase = StringUtilities::Convert(varsIterator->first, toupper);
		if (autoOverrides.find(varNameInLowCase) != autoOverrides.end())
		{
			targetEnvironment._vars[varsIterator->first] = varsIterator->second;
		}
	}

	return targetEnvironment;
}

Environment::~Environment()
{
	for (auto environmentBlockIterator = _environmentBlocks.begin(); environmentBlockIterator != _environmentBlocks.end(); ++environmentBlockIterator)
	{
		delete *environmentBlockIterator;
	}

	_environmentBlocks.clear();
}

void Environment::CreateVariableMap(LPVOID environment)
{
	_vars.clear();

	auto curVar = static_cast<LPCWSTR>(environment);
	size_t len;
	do
	{
		wstring curVarValue(curVar);
		len = curVarValue.size();
		if (len == 0)
		{
			continue;
		}

		curVar += len + 1;
		wsmatch matchResult;
		if (!regex_search(curVarValue, matchResult, EnvVarRegex))
		{
			continue;
		}

		auto envName = matchResult._At(1).str();
		auto envValue = matchResult._At(2).str();
		_vars[envName] = envValue;
		_empty = false;
	} while (len > 0);
}

LPVOID* Environment::CreateEnvironmentFromMap()
{
	size_t size = 0;
	for (auto varsIterator = _vars.begin(); varsIterator != _vars.end(); ++varsIterator)
	{
		size += varsIterator->first.size() + varsIterator->second.size() + 2;
	}

	size++;

	auto environment = new _TCHAR[size];
	memset(environment, 0, sizeof(_TCHAR) * size);

	size_t pointer = 0;
	for (auto varsIterator = _vars.begin(); varsIterator != _vars.end(); ++varsIterator)
	{
		auto curSize = varsIterator->first.size();
		memcpy(environment + pointer, varsIterator->first.c_str(), curSize * sizeof(_TCHAR));
		pointer += curSize;

		environment[pointer] = _TCHAR('=');
		pointer ++;

		curSize = varsIterator->second.size();
		memcpy(environment + pointer, varsIterator->second.c_str(), curSize * sizeof(_TCHAR));
		pointer += curSize;

		pointer ++;
	}

	return reinterpret_cast<LPVOID*>(environment);
}

LPVOID* Environment::CreateEnvironment()
{
	if(_empty)
	{
		return nullptr;
	}

	auto environment = CreateEnvironmentFromMap();
	_environmentBlocks.push_back(environment);
	return environment;
}

wstring Environment::TryGetValue(wstring variableName)
{
	auto curVarNameInLowCase = StringUtilities::Convert(variableName, tolower);
	for (auto varsIterator = _vars.begin(); varsIterator != _vars.end(); ++varsIterator)
	{
		auto varNameInLowCase = StringUtilities::Convert(varsIterator->first, tolower);
		if (varNameInLowCase == curVarNameInLowCase)
		{
			return varsIterator->second;
		}
	}

	return L"";
}

set<wstring> Environment::GetAutoOverrides()
{
	return AutoOverrides;
}<|endoftext|>


<commit_before>// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - -O1 %s | \
// RUN:     FileCheck -check-prefixes=CHECK,CHECK-OLD %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin -new-struct-path-tbaa \
// RUN:     -emit-llvm -o - -O1 %s | \
// RUN:     FileCheck -check-prefixes=CHECK,CHECK-NEW %s
//
// Check that we generate TBAA metadata for struct copies correctly.

struct A {
  short s;
  int i;
  char c;
  int j;
};

typedef A __attribute__((may_alias)) AA;

void copy(A *a1, A *a2) {
// CHECK-LABEL: _Z4copyP1AS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias align 4 dereferenceable(16) %{{.*}}, i8* noalias align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_A:![0-9]*]]
  *a1 = *a2;
}

struct B {
  char c;
  A a;
  int i;
};

void copy2(B *b1, B *b2) {
// CHECK-LABEL: _Z5copy2P1BS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias align 4 dereferenceable(24) %{{.*}}, i8* noalias align 4 dereferenceable(24) %{{.*}}, i64 24, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS2:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_B:![0-9]*]]
  *b1 = *b2;
}

struct S {
  _Complex char cc;
  _Complex int ci;
};

union U {
  _Complex int ci;
  S s;
};

void copy3(U *u1, U *u2) {
// CHECK-LABEL: _Z5copy3P1US0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias align 4 dereferenceable(12) %{{.*}}, i8* noalias align 4 dereferenceable(12) %{{.*}}, i64 12, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS3:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_U:![0-9]*]]
  *u1 = *u2;
}

// Make sure that zero-length bitfield works.
struct C {
  char a;
  int : 0;  // Shall not be ignored; see r185018.
  char b;
  char c;
} __attribute__((ms_struct));

void copy4(C *c1, C *c2) {
// CHECK-LABEL: _Z5copy4P1CS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias align 1 dereferenceable(3) {{.*}}, i8* noalias align 1 dereferenceable(3) {{.*}}, i64 3, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS4:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_C:![0-9]*]]
  *c1 = *c2;
}

struct D {
  char a;
  int : 0;
  char b;
  char c;
};

void copy5(D *d1, D *d2) {
// CHECK-LABEL: _Z5copy5P1DS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias align 1 dereferenceable(6) {{.*}}, i8* noalias align 1 dereferenceable(6) {{.*}}, i64 6, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS5:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_D:![0-9]*]]
  *d1 = *d2;
}

void copy6(AA *a1, A *a2) {
// CHECK-LABEL: _Z5copy6P1AS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias align 4 dereferenceable(16) %{{.*}}, i8* noalias align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS]]
// CHECK-NEW-SAME: !tbaa [[TAG_char:![0-9]*]]
  *a1 = *a2;
}

void copy7(A *a1, AA *a2) {
// CHECK-LABEL: _Z5copy7P1AS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias align 4 dereferenceable(16) %{{.*}}, i8* noalias align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS]]
// CHECK-NEW-SAME: !tbaa [[TAG_char]]
  *a1 = *a2;
}

// CHECK-OLD: [[TS]] = !{i64 0, i64 2, !{{.*}}, i64 4, i64 4, !{{.*}}, i64 8, i64 1, !{{.*}}, i64 12, i64 4, !{{.*}}}
// CHECK-OLD: [[CHAR:!.*]] = !{!"omnipotent char", !{{.*}}}
// CHECK-OLD: [[TAG_INT:!.*]] = !{[[INT:!.*]], [[INT]], i64 0}
// CHECK-OLD: [[INT]] = !{!"int", [[CHAR]]
// CHECK-OLD: [[TAG_CHAR:!.*]] = !{[[CHAR]], [[CHAR]], i64 0}
// (offset, size) = (0,1) char; (4,2) short; (8,4) int; (12,1) char; (16,4) int; (20,4) int
// CHECK-OLD: [[TS2]] = !{i64 0, i64 1, !{{.*}}, i64 4, i64 2, !{{.*}}, i64 8, i64 4, !{{.*}}, i64 12, i64 1, !{{.*}}, i64 16, i64 4, {{.*}}, i64 20, i64 4, {{.*}}}
// (offset, size) = (0,8) char; (0,2) char; (4,8) char
// CHECK-OLD: [[TS3]] = !{i64 0, i64 8, !{{.*}}, i64 0, i64 2, !{{.*}}, i64 4, i64 8, !{{.*}}}
// CHECK-OLD: [[TS4]] = !{i64 0, i64 1, [[TAG_CHAR]], i64 1, i64 1, [[TAG_CHAR]], i64 2, i64 1, [[TAG_CHAR]]}
// CHECK-OLD: [[TS5]] = !{i64 0, i64 1, [[TAG_CHAR]], i64 4, i64 1, [[TAG_CHAR]], i64 5, i64 1, [[TAG_CHAR]]}

// CHECK-NEW-DAG: [[TYPE_char:!.*]] = !{{{.*}}, i64 1, !"omnipotent char"}
// CHECK-NEW-DAG: [[TAG_char]] = !{[[TYPE_char]], [[TYPE_char]], i64 0, i64 0}
// CHECK-NEW-DAG: [[TYPE_short:!.*]] = !{[[TYPE_char]], i64 2, !"short"}
// CHECK-NEW-DAG: [[TYPE_int:!.*]] = !{[[TYPE_char]], i64 4, !"int"}
// CHECK-NEW-DAG: [[TYPE_A:!.*]] = !{[[TYPE_char]], i64 16, !"_ZTS1A", [[TYPE_short]], i64 0, i64 2, [[TYPE_int]], i64 4, i64 4, [[TYPE_char]], i64 8, i64 1, [[TYPE_int]], i64 12, i64 4}
// CHECK-NEW-DAG: [[TAG_A]] = !{[[TYPE_A]], [[TYPE_A]], i64 0, i64 16}
// CHECK-NEW-DAG: [[TYPE_B:!.*]] = !{[[TYPE_char]], i64 24, !"_ZTS1B", [[TYPE_char]], i64 0, i64 1, [[TYPE_A]], i64 4, i64 16, [[TYPE_int]], i64 20, i64 4}
// CHECK-NEW-DAG: [[TAG_B]] = !{[[TYPE_B]], [[TYPE_B]], i64 0, i64 24}
// CHECK-NEW-DAG: [[TAG_U]] = !{[[TYPE_char]], [[TYPE_char]], i64 0, i64 12}
// CHECK-NEW-DAG: [[TYPE_C:!.*]] = !{[[TYPE_char]], i64 3, !"_ZTS1C", [[TYPE_char]], i64 0, i64 1, [[TYPE_char]], i64 1, i64 1, [[TYPE_char]], i64 2, i64 1}
// CHECK-NEW-DAG: [[TAG_C]] = !{[[TYPE_C]], [[TYPE_C]], i64 0, i64 3}
// CHECK-NEW-DAG: [[TYPE_D:!.*]] = !{[[TYPE_char]], i64 6, !"_ZTS1D", [[TYPE_char]], i64 0, i64 1, [[TYPE_char]], i64 4, i64 1, [[TYPE_char]], i64 5, i64 1}
// CHECK-NEW-DAG: [[TAG_D]] = !{[[TYPE_D]], [[TYPE_D]], i64 0, i64 6}
<commit_msg>[NFC] Updated tests after r368875<commit_after>// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - -O1 %s | \
// RUN:     FileCheck -check-prefixes=CHECK,CHECK-OLD %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin -new-struct-path-tbaa \
// RUN:     -emit-llvm -o - -O1 %s | \
// RUN:     FileCheck -check-prefixes=CHECK,CHECK-NEW %s
//
// Check that we generate TBAA metadata for struct copies correctly.

struct A {
  short s;
  int i;
  char c;
  int j;
};

typedef A __attribute__((may_alias)) AA;

void copy(A *a1, A *a2) {
// CHECK-LABEL: _Z4copyP1AS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(16) %{{.*}}, i8* align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_A:![0-9]*]]
  *a1 = *a2;
}

struct B {
  char c;
  A a;
  int i;
};

void copy2(B *b1, B *b2) {
// CHECK-LABEL: _Z5copy2P1BS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(24) %{{.*}}, i8* align 4 dereferenceable(24) %{{.*}}, i64 24, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS2:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_B:![0-9]*]]
  *b1 = *b2;
}

struct S {
  _Complex char cc;
  _Complex int ci;
};

union U {
  _Complex int ci;
  S s;
};

void copy3(U *u1, U *u2) {
// CHECK-LABEL: _Z5copy3P1US0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(12) %{{.*}}, i8* align 4 dereferenceable(12) %{{.*}}, i64 12, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS3:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_U:![0-9]*]]
  *u1 = *u2;
}

// Make sure that zero-length bitfield works.
struct C {
  char a;
  int : 0;  // Shall not be ignored; see r185018.
  char b;
  char c;
} __attribute__((ms_struct));

void copy4(C *c1, C *c2) {
// CHECK-LABEL: _Z5copy4P1CS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 dereferenceable(3) {{.*}}, i8* align 1 dereferenceable(3) {{.*}}, i64 3, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS4:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_C:![0-9]*]]
  *c1 = *c2;
}

struct D {
  char a;
  int : 0;
  char b;
  char c;
};

void copy5(D *d1, D *d2) {
// CHECK-LABEL: _Z5copy5P1DS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 dereferenceable(6) {{.*}}, i8* align 1 dereferenceable(6) {{.*}}, i64 6, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS5:!.*]]
// CHECK-NEW-SAME: !tbaa [[TAG_D:![0-9]*]]
  *d1 = *d2;
}

void copy6(AA *a1, A *a2) {
// CHECK-LABEL: _Z5copy6P1AS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(16) %{{.*}}, i8* align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS]]
// CHECK-NEW-SAME: !tbaa [[TAG_char:![0-9]*]]
  *a1 = *a2;
}

void copy7(A *a1, AA *a2) {
// CHECK-LABEL: _Z5copy7P1AS0_
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(16) %{{.*}}, i8* align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false)
// CHECK-OLD-SAME: !tbaa.struct [[TS]]
// CHECK-NEW-SAME: !tbaa [[TAG_char]]
  *a1 = *a2;
}

// CHECK-OLD: [[TS]] = !{i64 0, i64 2, !{{.*}}, i64 4, i64 4, !{{.*}}, i64 8, i64 1, !{{.*}}, i64 12, i64 4, !{{.*}}}
// CHECK-OLD: [[CHAR:!.*]] = !{!"omnipotent char", !{{.*}}}
// CHECK-OLD: [[TAG_INT:!.*]] = !{[[INT:!.*]], [[INT]], i64 0}
// CHECK-OLD: [[INT]] = !{!"int", [[CHAR]]
// CHECK-OLD: [[TAG_CHAR:!.*]] = !{[[CHAR]], [[CHAR]], i64 0}
// (offset, size) = (0,1) char; (4,2) short; (8,4) int; (12,1) char; (16,4) int; (20,4) int
// CHECK-OLD: [[TS2]] = !{i64 0, i64 1, !{{.*}}, i64 4, i64 2, !{{.*}}, i64 8, i64 4, !{{.*}}, i64 12, i64 1, !{{.*}}, i64 16, i64 4, {{.*}}, i64 20, i64 4, {{.*}}}
// (offset, size) = (0,8) char; (0,2) char; (4,8) char
// CHECK-OLD: [[TS3]] = !{i64 0, i64 8, !{{.*}}, i64 0, i64 2, !{{.*}}, i64 4, i64 8, !{{.*}}}
// CHECK-OLD: [[TS4]] = !{i64 0, i64 1, [[TAG_CHAR]], i64 1, i64 1, [[TAG_CHAR]], i64 2, i64 1, [[TAG_CHAR]]}
// CHECK-OLD: [[TS5]] = !{i64 0, i64 1, [[TAG_CHAR]], i64 4, i64 1, [[TAG_CHAR]], i64 5, i64 1, [[TAG_CHAR]]}

// CHECK-NEW-DAG: [[TYPE_char:!.*]] = !{{{.*}}, i64 1, !"omnipotent char"}
// CHECK-NEW-DAG: [[TAG_char]] = !{[[TYPE_char]], [[TYPE_char]], i64 0, i64 0}
// CHECK-NEW-DAG: [[TYPE_short:!.*]] = !{[[TYPE_char]], i64 2, !"short"}
// CHECK-NEW-DAG: [[TYPE_int:!.*]] = !{[[TYPE_char]], i64 4, !"int"}
// CHECK-NEW-DAG: [[TYPE_A:!.*]] = !{[[TYPE_char]], i64 16, !"_ZTS1A", [[TYPE_short]], i64 0, i64 2, [[TYPE_int]], i64 4, i64 4, [[TYPE_char]], i64 8, i64 1, [[TYPE_int]], i64 12, i64 4}
// CHECK-NEW-DAG: [[TAG_A]] = !{[[TYPE_A]], [[TYPE_A]], i64 0, i64 16}
// CHECK-NEW-DAG: [[TYPE_B:!.*]] = !{[[TYPE_char]], i64 24, !"_ZTS1B", [[TYPE_char]], i64 0, i64 1, [[TYPE_A]], i64 4, i64 16, [[TYPE_int]], i64 20, i64 4}
// CHECK-NEW-DAG: [[TAG_B]] = !{[[TYPE_B]], [[TYPE_B]], i64 0, i64 24}
// CHECK-NEW-DAG: [[TAG_U]] = !{[[TYPE_char]], [[TYPE_char]], i64 0, i64 12}
// CHECK-NEW-DAG: [[TYPE_C:!.*]] = !{[[TYPE_char]], i64 3, !"_ZTS1C", [[TYPE_char]], i64 0, i64 1, [[TYPE_char]], i64 1, i64 1, [[TYPE_char]], i64 2, i64 1}
// CHECK-NEW-DAG: [[TAG_C]] = !{[[TYPE_C]], [[TYPE_C]], i64 0, i64 3}
// CHECK-NEW-DAG: [[TYPE_D:!.*]] = !{[[TYPE_char]], i64 6, !"_ZTS1D", [[TYPE_char]], i64 0, i64 1, [[TYPE_char]], i64 4, i64 1, [[TYPE_char]], i64 5, i64 1}
// CHECK-NEW-DAG: [[TAG_D]] = !{[[TYPE_D]], [[TYPE_D]], i64 0, i64 6}
<|endoftext|>


<commit_before>#include <UnitTest++/UnitTest++.h>

#include <memory>
#include <sstream>
#include <utility>

#include "Article.h"
#include "JsonSerializer.h"

SUITE(ArticleJsonSerializerTests)
{
  using namespace WikiWalker;

#define WW_PROTOCOL_HEADER \
  R"("program":"wikiwalker","scheme-version":2,"ArticleCollection":)"

  TEST(WriteUnanalyzedArticleWithoutLinks_LinksIsNull)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;
    ac.add(std::make_shared<Article>("Farm"));

    atj.serialize(ac, oss);

    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{"
                R"("Farm":{"forward_links":null})"
                "}"
                "}",
                oss.str());
  }

  TEST(WriteAnalyzedArticleWithoutLinks_LinksIsEmptyArray)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    auto a = std::make_shared<Article>("Farm");
    ac.add(a);
    a->analyzed(true);

    atj.serialize(ac, oss);

    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{"
                R"("Farm":{"forward_links":[]})"
                "}"
                "}",
                oss.str());
  }

  TEST(WriteArticleWithOneLink)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    // yes, only a is inserted, since we want to emulate article-only
    auto a = std::make_shared<Article>("Farm");
    ac.add(a);

    auto linked = std::make_shared<Article>("Animal");
    a->addLink(linked);

    atj.serialize(ac, oss);
    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{"
                R"("Farm":{"forward_links":["Animal"]})"
                "}"
                "}",
                oss.str());
  }

  TEST(WriteArticleWithMultipleLinks)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    // yes, only a is inserted, since we want to emulate article-only
    auto a = std::make_shared<Article>("Farm");
    ac.add(a);

    auto al1 = std::make_shared<Article>("Animal"),
         al2 = std::make_shared<Article>("Pig"),
         al3 = std::make_shared<Article>("Equality");

    a->addLink(al1);
    a->addLink(al2);
    a->addLink(al3);

    atj.serialize(ac, oss);

    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{"
                R"("Farm":{"forward_links":["Animal","Pig","Equality"]})"
                "}"
                "}",
                oss.str());
  }

  TEST(WriteEmptyArticleCollection)
  {
    JsonSerializer atj;
    ArticleCollection ac;
    std::ostringstream oss;

    atj.serialize(ac, oss);

    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{}"
                "}",
                oss.str());
  }

  TEST(WriteArticleCollection_OneUnanalyzedArticleWithoutLinks_LinksIsNull)
  {
    JsonSerializer atj;
    ArticleCollection ac;
    std::ostringstream oss;

    auto linked = std::make_shared<Article>("Foo");
    ac.add(linked);

    atj.serialize(ac, oss);

    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{"
                R"("Foo":{"forward_links":null})"
                "}"
                "}",
                oss.str());
  }

  TEST(WriteArticleCollection_OneAnalyzedArticleWithoutLinks_LinksIsEmptyArray)
  {
    JsonSerializer atj;
    ArticleCollection ac;
    std::ostringstream oss;

    auto a = std::make_shared<Article>("Foo");
    a->analyzed(true);
    ac.add(a);

    atj.serialize(ac, oss);

    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{"
                R"("Foo":{"forward_links":[]})"
                "}"
                "}",
                oss.str());
  }

  TEST(
      WriteArticleCollection_MultipleArticles_WithMultipleLinks_MatchesExpected)
  {
    JsonSerializer atj;
    ArticleCollection ac;
    std::ostringstream oss;

    auto a = std::make_shared<Article>("Foo");
    auto b = std::make_shared<Article>("Bar");
    auto c = std::make_shared<Article>("Baz");
    a->addLink(b);
    b->addLink(a);
    b->addLink(c);
    ac.add(a);
    ac.add(b);
    ac.add(c);

    atj.serialize(ac, oss);

    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{"
                R"("Bar":{"forward_links":["Foo","Baz"]},)"
                R"("Baz":{"forward_links":null},)"
                R"("Foo":{"forward_links":["Bar"]})"
                "}"
                "}",
                oss.str());
  }

  TEST(SerializeArticleWithOnlyNullptr_NullptrWillBeSkipped)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    // yes, only a is inserted, since we want to emulate article-only
    auto a = std::make_shared<Article>("Farm");
    ac.add(a);

    {
      // will be skipped on serialization, since it'll become nullptr
      auto linked = std::make_shared<Article>("Animal");
      a->addLink(linked);
    }

    atj.serialize(ac, oss);

    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{"
                R"("Farm":{"forward_links":[]})"
                "}"
                "}",
                oss.str());
  }

  TEST(SerializeArticleWithValidArticleAndANullptr_NullptrWillBeSkipped)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    // yes, only a is inserted, since we want to emulate article-only
    auto a = std::make_shared<Article>("Farm");
    ac.add(a);

    {
      // will be skipped on serialization, since it'll become nullptr
      auto linked = std::make_shared<Article>("Animal");
      a->addLink(linked);
    }

    auto linked2 = std::make_shared<Article>("Barn");
    a->addLink(linked2);

    atj.serialize(ac, oss);

    CHECK_EQUAL("{" WW_PROTOCOL_HEADER
                "{"
                R"("Farm":{"forward_links":["Barn"]})"
                "}"
                "}",
                oss.str());
  }
}
<commit_msg>Use string::fine instead of direct compare<commit_after>#include <UnitTest++/UnitTest++.h>

#include <memory>
#include <sstream>
#include <string>
#include <utility>

#include "Article.h"
#include "JsonSerializer.h"
#include "StringUtils.h"

SUITE(ArticleJsonSerializerTests)
{
  using namespace WikiWalker;

#define WW_PROTOCOL_COLLECTION_KEY R"("ArticleCollection":)"
#define WW_PROTOCOL_HEADER_1 R"("program":"wikiwalker")"
#define WW_PROTOCOL_HEADER_2 R"("scheme-version":2)"

  TEST(WriteUnanalyzedArticleWithoutLinks_LinksIsNull)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;
    ac.add(std::make_shared<Article>("Farm"));

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_COLLECTION_KEY
                         R"({"Farm":{"forward_links":null}})") !=
          std::string::npos);
  }

  TEST(WriteAnalyzedArticleWithoutLinks_LinksIsEmptyArray)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    auto a = std::make_shared<Article>("Farm");
    ac.add(a);
    a->analyzed(true);

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_COLLECTION_KEY
                         R"({"Farm":{"forward_links":[]}})") !=
          std::string::npos);
  }

  TEST(WriteArticleWithOneLink)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    // yes, only a is inserted, since we want to emulate article-only
    auto a = std::make_shared<Article>("Farm");
    ac.add(a);

    auto linked = std::make_shared<Article>("Animal");
    a->addLink(linked);

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_COLLECTION_KEY
                         R"({"Farm":{"forward_links":["Animal"]}})") !=
          std::string::npos);
  }

  TEST(WriteArticleWithMultipleLinks)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    // yes, only a is inserted, since we want to emulate article-only
    auto a = std::make_shared<Article>("Farm");
    ac.add(a);

    auto al1 = std::make_shared<Article>("Animal"),
         al2 = std::make_shared<Article>("Pig"),
         al3 = std::make_shared<Article>("Equality");

    a->addLink(al1);
    a->addLink(al2);
    a->addLink(al3);

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(
              WW_PROTOCOL_COLLECTION_KEY
              R"({"Farm":{"forward_links":["Animal","Pig","Equality"]}})") !=
          std::string::npos);
  }

  TEST(WriteEmptyArticleCollection)
  {
    JsonSerializer atj;
    ArticleCollection ac;
    std::ostringstream oss;

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_COLLECTION_KEY "{}") != std::string::npos);
  }

  TEST(WriteArticleCollection_OneUnanalyzedArticleWithoutLinks_LinksIsNull)
  {
    JsonSerializer atj;
    ArticleCollection ac;
    std::ostringstream oss;

    auto linked = std::make_shared<Article>("Foo");
    ac.add(linked);

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_COLLECTION_KEY
                         R"({"Foo":{"forward_links":null}})") !=
          std::string::npos);
  }

  TEST(WriteArticleCollection_OneAnalyzedArticleWithoutLinks_LinksIsEmptyArray)
  {
    JsonSerializer atj;
    ArticleCollection ac;
    std::ostringstream oss;

    auto a = std::make_shared<Article>("Foo");
    a->analyzed(true);
    ac.add(a);

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_COLLECTION_KEY
                         R"({"Foo":{"forward_links":[]}})") !=
          std::string::npos);
  }

  TEST(
      WriteArticleCollection_MultipleArticles_WithMultipleLinks_MatchesExpected)
  {
    JsonSerializer atj;
    ArticleCollection ac;
    std::ostringstream oss;

    auto a = std::make_shared<Article>("Foo");
    auto b = std::make_shared<Article>("Bar");
    auto c = std::make_shared<Article>("Baz");
    a->addLink(b);
    b->addLink(a);
    b->addLink(c);
    ac.add(a);
    ac.add(b);
    ac.add(c);

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_COLLECTION_KEY
                         "{"
                         R"("Bar":{"forward_links":["Foo","Baz"]},)"
                         R"("Baz":{"forward_links":null},)"
                         R"("Foo":{"forward_links":["Bar"]})"
                         "}") != std::string::npos);
  }

  TEST(SerializeArticleWithOnlyNullptr_NullptrWillBeSkipped)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    // yes, only a is inserted, since we want to emulate article-only
    auto a = std::make_shared<Article>("Farm");
    ac.add(a);

    {
      // will be skipped on serialization, since it'll become nullptr
      auto linked = std::make_shared<Article>("Animal");
      a->addLink(linked);
    }

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_COLLECTION_KEY
                         R"({"Farm":{"forward_links":[]}})") !=
          std::string::npos);
  }

  TEST(SerializeArticleWithValidArticleAndANullptr_NullptrWillBeSkipped)
  {
    JsonSerializer atj;
    std::ostringstream oss;
    ArticleCollection ac;

    // yes, only a is inserted, since we want to emulate article-only
    auto a = std::make_shared<Article>("Farm");
    ac.add(a);

    {
      // will be skipped on serialization, since it'll become nullptr
      auto linked = std::make_shared<Article>("Animal");
      a->addLink(linked);
    }

    auto linked2 = std::make_shared<Article>("Barn");
    a->addLink(linked2);

    atj.serialize(ac, oss);

    auto serString = oss.str();
    CHECK(serString.find(WW_PROTOCOL_HEADER_1) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_HEADER_2) != std::string::npos);
    CHECK(serString.find(WW_PROTOCOL_COLLECTION_KEY
                         R"({"Farm":{"forward_links":["Barn"]}})") !=
          std::string::npos);
  }
}
<|endoftext|>


<commit_before>#include "atomic.hh"
#include "locks.hh"
#include <pthread.h>
#include <unistd.h>
#include "assert.h"
#define NUM_THREADS 90
#define NUM_ITEMS 100000

struct thread_args {
    SyncObject mutex;
    SyncObject gate;
    AtomicQueue<int> queue;
    int counter;
};

void *launch_consumer_thread(void *arg) {
    struct thread_args *args = static_cast<struct thread_args *>(arg);
    int count(0);
    std::queue<int> outQueue;
    LockHolder lh(args->mutex);
    LockHolder lhg(args->gate);
    args->counter++;
    lhg.unlock();
    args->gate.notify();
    args->mutex.wait();
    lh.unlock();

    while(count < NUM_THREADS * NUM_ITEMS) {
        args->queue.size();
        args->queue.getAll(outQueue);
        while (!outQueue.empty()) {
            count++;
            outQueue.pop();
        }
    }
    sleep(1);
    assert(args->queue.empty());
    assert(outQueue.empty());
    return static_cast<void *>(0);
}

void *launch_test_thread(void *arg) {
    struct thread_args *args = static_cast<struct thread_args *>(arg);
    int i(0);
    LockHolder lh(args->mutex);
    LockHolder lhg(args->gate);
    args->counter++;
    lhg.unlock();
    args->gate.notify();
    args->mutex.wait();
    lh.unlock();
    for (i = 0; i < NUM_ITEMS; ++i) {
        args->queue.push(i);
    }

    return static_cast<void *>(0);
}

int main() {
    pthread_t threads[NUM_THREADS];
    pthread_t consumer;
    int i(0), rc(0), result(-1);
    struct thread_args args;

    alarm(60);

    args.counter = 0;

    rc = pthread_create(&consumer, NULL, launch_consumer_thread, &args);
    assert(rc == 0);

    for (i = 0; i < NUM_THREADS; ++i) {
        rc = pthread_create(&threads[i], NULL, launch_test_thread, &args);
        assert(rc == 0);
    }

    // Wait for all threads to reach the starting gate
    int counter;
    while (true) {
        LockHolder lh(args.gate);
        counter = args.counter;
        if (counter == NUM_THREADS + 1) {
            break;
        }
        args.gate.wait();
    }
    args.mutex.notify();

    for (i = 0; i < NUM_THREADS; ++i) {
        rc = pthread_join(threads[i], reinterpret_cast<void **>(&result));
        assert(rc == 0);
        assert(result == 0);
    }

    rc = pthread_join(consumer, reinterpret_cast<void **>(&result));
    assert(rc == 0);
    assert(result == 0);
    assert(args.queue.empty());
}
<commit_msg>Remove compile warnings<commit_after>#include "atomic.hh"
#include "locks.hh"
#include <pthread.h>
#include <unistd.h>
#include "assert.h"
#define NUM_THREADS 90
#define NUM_ITEMS 100000

struct thread_args {
    SyncObject mutex;
    SyncObject gate;
    AtomicQueue<int> queue;
    int counter;
};

extern "C" {
static void *launch_consumer_thread(void *arg) {
    struct thread_args *args = static_cast<struct thread_args *>(arg);
    int count(0);
    std::queue<int> outQueue;
    LockHolder lh(args->mutex);
    LockHolder lhg(args->gate);
    args->counter++;
    lhg.unlock();
    args->gate.notify();
    args->mutex.wait();
    lh.unlock();

    while(count < NUM_THREADS * NUM_ITEMS) {
        args->queue.size();
        args->queue.getAll(outQueue);
        while (!outQueue.empty()) {
            count++;
            outQueue.pop();
        }
    }
    sleep(1);
    assert(args->queue.empty());
    assert(outQueue.empty());
    return static_cast<void *>(0);
}

static void *launch_test_thread(void *arg) {
    struct thread_args *args = static_cast<struct thread_args *>(arg);
    int i(0);
    LockHolder lh(args->mutex);
    LockHolder lhg(args->gate);
    args->counter++;
    lhg.unlock();
    args->gate.notify();
    args->mutex.wait();
    lh.unlock();
    for (i = 0; i < NUM_ITEMS; ++i) {
        args->queue.push(i);
    }

    return static_cast<void *>(0);
}
}

int main() {
    pthread_t threads[NUM_THREADS];
    pthread_t consumer;
    int i(0), rc(0), result(-1);
    struct thread_args args;

    alarm(60);

    args.counter = 0;

    rc = pthread_create(&consumer, NULL, launch_consumer_thread, &args);
    assert(rc == 0);

    for (i = 0; i < NUM_THREADS; ++i) {
        rc = pthread_create(&threads[i], NULL, launch_test_thread, &args);
        assert(rc == 0);
    }

    // Wait for all threads to reach the starting gate
    int counter;
    while (true) {
        LockHolder lh(args.gate);
        counter = args.counter;
        if (counter == NUM_THREADS + 1) {
            break;
        }
        args.gate.wait();
    }
    args.mutex.notify();

    for (i = 0; i < NUM_THREADS; ++i) {
        rc = pthread_join(threads[i], reinterpret_cast<void **>(&result));
        assert(rc == 0);
        assert(result == 0);
    }

    rc = pthread_join(consumer, reinterpret_cast<void **>(&result));
    assert(rc == 0);
    assert(result == 0);
    assert(args.queue.empty());
}
<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by the Free  *
* Software Foundation; either version 2 of the License, or (at your option)   *
* any later version.                                                          *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program; if not, write to the Free Software Foundation, Inc., 51  *
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.                   *
*******************************************************************************
*                            SOFA :: Applications                             *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

#include "Sofa_test.h"
#include<sofa/helper/system/SetDirectory.h>
#include <sofa/helper/system/FileRepository.h>
#include <sofa/component/init.h>
#include <sofa/core/ExecParams.h>

//Including Simulation
#include <sofa/simulation/common/Simulation.h>
#include <sofa/simulation/graph/DAGSimulation.h>
#include <sofa/simulation/common/Node.h>

// Including constraint, force and mass
#include <sofa/component/topology/RegularGridTopology.h>
#include <sofa/component/projectiveconstraintset/BilinearMovementConstraint.h>
#include <sofa/component/container/MechanicalObject.h>
#include <sofa/component/interactionforcefield/MeshSpringForceField.h>
#include <sofa/component/forcefield/TetrahedronFEMForceField.h>
#include <sofa/core/MechanicalParams.h>

#include <sofa/defaulttype/VecTypes.h>
#include <plugins/SceneCreator/SceneCreator.h>

namespace sofa {

using std::cout;
using std::cerr;
using std::endl;
using namespace component;
using namespace defaulttype;
using namespace modeling;

/**  Test suite for ProjectToLineConstraint.
The test cases are defined in the #Test_Cases member group.
  */
template <typename _DataTypes>
struct Patch_test : public Sofa_test<typename _DataTypes::Real>
{
    typedef _DataTypes DataTypes;
    typedef typename DataTypes::VecCoord VecCoord;
    typedef typename DataTypes::VecDeriv VecDeriv;
    typedef typename DataTypes::Deriv Deriv;
    typedef projectiveconstraintset::BilinearMovementConstraint<DataTypes> BilinearMovementConstraint;
    typedef container::MechanicalObject<DataTypes> MechanicalObject;
    typedef typename component::interactionforcefield::MeshSpringForceField<DataTypes> MeshSpringForceField;
    typedef typename component::forcefield::TetrahedronFEMForceField<DataTypes> TetraForceField;

    /// Root of the scene graph
    simulation::Node::SPtr root;
    /// Simulation
    simulation::Simulation* simulation;
    // Structure which contains current node and pointers to the mechanical object and the bilinear constraint
    PatchTestStruct<DataTypes> patchStruct;

    // Create the context for the scene
    void SetUp()
    {
        // Init simulation
        sofa::component::init();
        sofa::simulation::setSimulation(simulation = new sofa::simulation::graph::DAGSimulation());

         root = simulation::getSimulation()->createNewGraph("root");
    }

    // Create a scene with a 2D regular grid and a bilinear constraint
    void createScene2DRegularGrid()
    {
        // Initialization
        Vec<6,Real> box (-0.1,-0.1,0,1.1,1.1,0);
        helper::vector< Vec<6,Real> > vecBox;
        vecBox.push_back(box);

        // Create a scene with a regular grid
        patchStruct = createRegularGridScene<DataTypes>(
                        root,  // attached to the root node
                        Vec<3,SReal>(0,0,0), // Start point of regular grid
                        Vec<3,SReal>(1,1,0), // End point of regular grid
                        5,5,1,  // Resolution of the regular grid
                        vecBox, // BoxRoi to find all mesh points
                        Vec<6,Real>(-0.1,-0.1,0,1.1,1.1,0), // inclusive box of pair box roi
                        Vec<6,Real>(0.1,0.1,0,0.9,0.9,0)); // included box of pair box roi

        simulation::Node::SPtr SquareNode = patchStruct.SquareNode;

        //Force field for 2D Grid
        MeshSpringForceField::SPtr meshSpringForceField = addNew<MeshSpringForceField> (SquareNode,"forceField");
        meshSpringForceField->setStiffness(10);

        // Set the corner movements of the bilinear constraint
        VecDeriv cornerMovementsData = patchStruct.bilinearConstraint->m_cornerMovements.getValue();
        cornerMovementsData.push_back(Deriv(0,0,0));
        cornerMovementsData.push_back(Deriv(0.01,-0.01,0));
        cornerMovementsData.push_back(Deriv(0,0,0));
        cornerMovementsData.push_back(Deriv(-0.01,0.01,0));
        patchStruct.bilinearConstraint->m_cornerMovements.setValue(cornerMovementsData);

    }

     // Create a scene with a 3D regular grid and a bilinear constraint
    void createScene3DRegularGrid()
    {
        // Initialization
        Vec<6,Real> box (-0.1,-0.1,-0.1,1.1,1.1,1.1);
        helper::vector< Vec<6,Real> > vecBox;
        vecBox.push_back(box);

        // Create a scene with a regular grid
        patchStruct = createRegularGridScene<DataTypes>(
                        root,  // attached to the root node
                        Vec<3,SReal>(0,0,0), // Start point of regular grid
                        Vec<3,SReal>(1,1,1), // End point of regular grid
                        5,5,5,  // Resolution of the regular grid
                        vecBox, // BoxRoi to find all mesh points
                        Vec<6,Real>(-0.1,-0.1,-0.1,1.1,1.1,1.1), // inclusive box of pair box roi
                        Vec<6,Real>(0.1,0.1,0.1,0.9,0.9,0.9)); // included box of pair box roi

        simulation::Node::SPtr SquareNode = patchStruct.SquareNode;

        // Force field for 3D Grid
        TetraForceField::SPtr tetraFEM = addNew<TetraForceField>(SquareNode,"forceField");
        tetraFEM->setMethod("polar");
        tetraFEM->setYoungModulus(20);
        tetraFEM->setPoissonRatio(0.4);

        // Set the corner movements of the bilinear constraint
        VecDeriv cornerMovementsData = patchStruct.bilinearConstraint->m_cornerMovements.getValue();
        cornerMovementsData.push_back(Deriv(0,0,0));
        cornerMovementsData.push_back(Deriv(0.01,-0.01,-0.01));
        cornerMovementsData.push_back(Deriv(0.01,0.01,-0.01));
        cornerMovementsData.push_back(Deriv(-0.01,0.01,-0.01));
        cornerMovementsData.push_back(Deriv(-0.01,-0.01,0.01));
        cornerMovementsData.push_back(Deriv(0.01,-0.01,0.01));
        cornerMovementsData.push_back(Deriv(0,0,0));
        cornerMovementsData.push_back(Deriv(-0.01,0.01,0.01));
        patchStruct.bilinearConstraint->m_cornerMovements.setValue(cornerMovementsData);

    }

    bool test_projectPosition()
    {
        // Init simulation
        sofa::simulation::getSimulation()->init(root.get());

        // Animate
        do
        {sofa::simulation::getSimulation()->animate(root.get(),0.5);}
        while(root->getAnimationLoop()->getTime() < 22);

        // Get the simulated final positions
        typename MechanicalObject::WriteVecCoord x = patchStruct.dofs->writePositions();

        // Compute the theoretical final positions
        VecCoord finalPos;
        patchStruct.bilinearConstraint->getFinalPositions( finalPos,*patchStruct.dofs->write(core::VecCoordId::position()) );

        // Compare the theoretical positions and the simulated positions
        bool succeed=true;
        for(size_t i=0; i<finalPos.size(); i++ )
        {
            if((finalPos[i]-x[i]).norm()>7e-4)
            {
                succeed = false;
                ADD_FAILURE() << "final Position of point " << i << " is wrong: " << x[i] << std::endl <<"the expected Position is " << finalPos[i] << "difference = " <<(finalPos[i]-x[i]).norm() << std::endl;
            }
        }
        return succeed;
    }

    void TearDown()
    {
        if (root!=NULL)
            sofa::simulation::getSimulation()->unload(root);
//        cerr<<"tearing down"<<endl;
    }

};

// Define the list of DataTypes to instanciate
using testing::Types;
typedef Types<
    Vec3Types,
    Vec3Types
> DataTypes; // the types to instanciate.

// Test suite for all the instanciations
TYPED_TEST_CASE(Patch_test, DataTypes);

// first test case
TYPED_TEST( Patch_test , patchTest2D )
{
   this->createScene2DRegularGrid();
   ASSERT_TRUE( this->test_projectPosition());
}

// second test case
TYPED_TEST( Patch_test , patchTest3D )
{
    this->createScene3DRegularGrid();
    ASSERT_TRUE( this->test_projectPosition());
}

} // namespace sofa
<commit_msg>r10542/sofa : FIX: try to fix compilation on Linux.<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by the Free  *
* Software Foundation; either version 2 of the License, or (at your option)   *
* any later version.                                                          *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program; if not, write to the Free Software Foundation, Inc., 51  *
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.                   *
*******************************************************************************
*                            SOFA :: Applications                             *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

#include "Sofa_test.h"
#include<sofa/helper/system/SetDirectory.h>
#include <sofa/helper/system/FileRepository.h>
#include <sofa/component/init.h>
#include <sofa/core/ExecParams.h>

//Including Simulation
#include <sofa/simulation/common/Simulation.h>
#include <sofa/simulation/graph/DAGSimulation.h>
#include <sofa/simulation/common/Node.h>

// Including constraint, force and mass
#include <sofa/component/topology/RegularGridTopology.h>
#include <sofa/component/projectiveconstraintset/BilinearMovementConstraint.h>
#include <sofa/component/container/MechanicalObject.h>
#include <sofa/component/interactionforcefield/MeshSpringForceField.h>
#include <sofa/component/forcefield/TetrahedronFEMForceField.h>
#include <sofa/core/MechanicalParams.h>

#include <sofa/defaulttype/VecTypes.h>
#include <plugins/SceneCreator/SceneCreator.h>

namespace sofa {

using std::cout;
using std::cerr;
using std::endl;
using namespace component;
using namespace defaulttype;
using namespace modeling;

/**  Test suite for ProjectToLineConstraint.
The test cases are defined in the #Test_Cases member group.
  */
template <typename _DataTypes>
struct Patch_test : public Sofa_test<typename _DataTypes::Real>
{
    typedef _DataTypes DataTypes;
    typedef typename DataTypes::VecCoord VecCoord;
    typedef typename DataTypes::VecDeriv VecDeriv;
    typedef typename DataTypes::Deriv Deriv;
    typedef projectiveconstraintset::BilinearMovementConstraint<DataTypes> BilinearMovementConstraint;
    typedef container::MechanicalObject<DataTypes> MechanicalObject;
    typedef typename component::interactionforcefield::MeshSpringForceField<DataTypes> MeshSpringForceField;
    typedef typename component::forcefield::TetrahedronFEMForceField<DataTypes> TetraForceField;

    /// Root of the scene graph
    simulation::Node::SPtr root;
    /// Simulation
    simulation::Simulation* simulation;
    // Structure which contains current node and pointers to the mechanical object and the bilinear constraint
    PatchTestStruct<DataTypes> patchStruct;

    // Create the context for the scene
    void SetUp()
    {
        // Init simulation
        sofa::component::init();
        sofa::simulation::setSimulation(simulation = new sofa::simulation::graph::DAGSimulation());

         root = simulation::getSimulation()->createNewGraph("root");
    }

    // Create a scene with a 2D regular grid and a bilinear constraint
    void createScene2DRegularGrid()
    {
        // Initialization
        Vec<6,SReal> box (-0.1,-0.1,0,1.1,1.1,0);
        helper::vector< Vec<6,SReal> > vecBox;
        vecBox.push_back(box);

        // Create a scene with a regular grid
        patchStruct = createRegularGridScene<DataTypes>(
                        root,  // attached to the root node
                        Vec<3,SReal>(0,0,0), // Start point of regular grid
                        Vec<3,SReal>(1,1,0), // End point of regular grid
                        5,5,1,  // Resolution of the regular grid
                        vecBox, // BoxRoi to find all mesh points
                        Vec<6,SReal>(-0.1,-0.1,0,1.1,1.1,0), // inclusive box of pair box roi
                        Vec<6,SReal>(0.1,0.1,0,0.9,0.9,0)); // included box of pair box roi

        simulation::Node::SPtr SquareNode = patchStruct.SquareNode;

        //Force field for 2D Grid
        typename MeshSpringForceField::SPtr meshSpringForceField = addNew<MeshSpringForceField> (SquareNode,"forceField");
        meshSpringForceField->setStiffness(10);

        // Set the corner movements of the bilinear constraint
        VecDeriv cornerMovementsData = patchStruct.bilinearConstraint->m_cornerMovements.getValue();
        cornerMovementsData.push_back(Deriv(0,0,0));
        cornerMovementsData.push_back(Deriv(0.01,-0.01,0));
        cornerMovementsData.push_back(Deriv(0,0,0));
        cornerMovementsData.push_back(Deriv(-0.01,0.01,0));
        patchStruct.bilinearConstraint->m_cornerMovements.setValue(cornerMovementsData);

    }

     // Create a scene with a 3D regular grid and a bilinear constraint
    void createScene3DRegularGrid()
    {
        // Initialization
        Vec<6,SReal> box (-0.1,-0.1,-0.1,1.1,1.1,1.1);
        helper::vector< Vec<6,SReal> > vecBox;
        vecBox.push_back(box);

        // Create a scene with a regular grid
        patchStruct = createRegularGridScene<DataTypes>(
                        root,  // attached to the root node
                        Vec<3,SReal>(0,0,0), // Start point of regular grid
                        Vec<3,SReal>(1,1,1), // End point of regular grid
                        5,5,5,  // Resolution of the regular grid
                        vecBox, // BoxRoi to find all mesh points
                        Vec<6,SReal>(-0.1,-0.1,-0.1,1.1,1.1,1.1), // inclusive box of pair box roi
                        Vec<6,SReal>(0.1,0.1,0.1,0.9,0.9,0.9)); // included box of pair box roi

        simulation::Node::SPtr SquareNode = patchStruct.SquareNode;

        // Force field for 3D Grid
        typename TetraForceField::SPtr tetraFEM = addNew<TetraForceField>(SquareNode,"forceField");
        tetraFEM->setMethod("polar");
        tetraFEM->setYoungModulus(20);
        tetraFEM->setPoissonRatio(0.4);

        // Set the corner movements of the bilinear constraint
        VecDeriv cornerMovementsData = patchStruct.bilinearConstraint->m_cornerMovements.getValue();
        cornerMovementsData.push_back(Deriv(0,0,0));
        cornerMovementsData.push_back(Deriv(0.01,-0.01,-0.01));
        cornerMovementsData.push_back(Deriv(0.01,0.01,-0.01));
        cornerMovementsData.push_back(Deriv(-0.01,0.01,-0.01));
        cornerMovementsData.push_back(Deriv(-0.01,-0.01,0.01));
        cornerMovementsData.push_back(Deriv(0.01,-0.01,0.01));
        cornerMovementsData.push_back(Deriv(0,0,0));
        cornerMovementsData.push_back(Deriv(-0.01,0.01,0.01));
        patchStruct.bilinearConstraint->m_cornerMovements.setValue(cornerMovementsData);

    }

    bool test_projectPosition()
    {
        // Init simulation
        sofa::simulation::getSimulation()->init(root.get());

        // Animate
        do
        {sofa::simulation::getSimulation()->animate(root.get(),0.5);}
        while(root->getAnimationLoop()->getTime() < 22);

        // Get the simulated final positions
        typename MechanicalObject::WriteVecCoord x = patchStruct.dofs->writePositions();

        // Compute the theoretical final positions
        VecCoord finalPos;
        patchStruct.bilinearConstraint->getFinalPositions( finalPos,*patchStruct.dofs->write(core::VecCoordId::position()) );

        // Compare the theoretical positions and the simulated positions
        bool succeed=true;
        for(size_t i=0; i<finalPos.size(); i++ )
        {
            if((finalPos[i]-x[i]).norm()>7e-4)
            {
                succeed = false;
                ADD_FAILURE() << "final Position of point " << i << " is wrong: " << x[i] << std::endl <<"the expected Position is " << finalPos[i] << "difference = " <<(finalPos[i]-x[i]).norm() << std::endl;
            }
        }
        return succeed;
    }

    void TearDown()
    {
        if (root!=NULL)
            sofa::simulation::getSimulation()->unload(root);
//        cerr<<"tearing down"<<endl;
    }

};

// Define the list of DataTypes to instanciate
using testing::Types;
typedef Types<
    Vec3Types,
    Vec3Types
> DataTypes; // the types to instanciate.

// Test suite for all the instanciations
TYPED_TEST_CASE(Patch_test, DataTypes);

// first test case
TYPED_TEST( Patch_test , patchTest2D )
{
   this->createScene2DRegularGrid();
   ASSERT_TRUE( this->test_projectPosition());
}

// second test case
TYPED_TEST( Patch_test , patchTest3D )
{
    this->createScene3DRegularGrid();
    ASSERT_TRUE( this->test_projectPosition());
}

} // namespace sofa
<|endoftext|>