<commit_before>//
// units.hpp
// units-cxx14
//
// Copyright (c) 2016 Félix Cloutier
//
// 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.
//

#ifndef UNITS_HPP
#define UNITS_HPP

#include <cstddef>
#include <type_traits>
#include <utility>

namespace detail
{
#pragma mark - Sequence type
	// (integer_sequence only works with integers, not enums)

	template<typename T, T... S>
	struct sequence
	{
		typedef T value_type;
		static constexpr size_t size = sizeof...(S);
	};

#pragma mark - Test if a sequence contains a value
	template<typename IntegerType, IntegerType I, IntegerType... S>
	struct sequence_contains_type;

	template<typename IntegerType, IntegerType I, IntegerType S1,
		IntegerType... Ss>
	struct sequence_contains_type<IntegerType, I, S1, Ss...>
		: std::conditional<I == S1,
			std::true_type,
			typename sequence_contains_type<IntegerType, I, Ss...>::type>::type
	{
	};

	template<typename IntegerType, IntegerType I>
	struct sequence_contains_type<IntegerType, I> : std::false_type
	{
	};

	template<typename IntegerType, IntegerType I, IntegerType... Is>
	constexpr bool sequence_contains(sequence<IntegerType, Is...>)
	{
		return sequence_contains_type<IntegerType, I, Is...>::value;
	}

#pragma mark - Combine sequences
	template<typename Seq1, typename Seq2>
	struct combine;

	template<typename IntegerType, IntegerType... S1, IntegerType... S2>
	struct combine<sequence<IntegerType, S1...>, sequence<IntegerType, S2...>>
	{
		typedef sequence<IntegerType, S1..., S2...> type;
	};

	template<typename Seq1, typename Seq2>
	constexpr auto combine_sequences(Seq1, Seq2)
	{
		return typename combine<Seq1, Seq2>::type{};
	}

#pragma mark - Remove an item
	template<typename Seq, typename IntegerType, IntegerType I>
	struct left_of;

	template<typename IntegerType, IntegerType I>
	struct left_of<sequence<IntegerType>, IntegerType, I>
	{
		typedef sequence<IntegerType> type;
	};

	template<typename IntegerType, IntegerType I, IntegerType S1,
		IntegerType... Ss>
	struct left_of<sequence<IntegerType, S1, Ss...>, IntegerType, I>
	{
		typedef typename std::conditional<I == S1,
			sequence<IntegerType>,
			typename combine<
				sequence<IntegerType, S1>,
				typename left_of<
					sequence<IntegerType, Ss...>,
					IntegerType, I>::type>::type>::type type;
	};

	template<typename Seq, typename IntegerType, IntegerType I>
	struct right_of;

	template<typename IntegerType, IntegerType I>
	struct right_of<sequence<IntegerType>, IntegerType, I>
	{
		typedef sequence<IntegerType> type;
	};

	template<typename IntegerType, IntegerType I, IntegerType S1,
		IntegerType... Ss>
	struct right_of<sequence<IntegerType, S1, Ss...>, IntegerType, I>
	{
		typedef typename std::conditional<I == S1,
			sequence<IntegerType, Ss...>,
			typename right_of<sequence<IntegerType, Ss...>,
		IntegerType, I>::type>::type type;
	};

	template<typename Seq, typename IntegerType, IntegerType I>
	struct removed_from
	{
		typedef typename combine<
			typename left_of<Seq, IntegerType, I>::type,
			typename right_of<Seq, IntegerType, I>::type>::type type;
	};

#pragma mark - Remove identical terms
	template<typename Seq1, typename Seq2>
	struct remove_intersection;

	template<typename IntegerType1, IntegerType1... Ones, typename IntegerType2>
	struct remove_intersection<
		sequence<IntegerType1, Ones...>,
		sequence<IntegerType2>>
	{
		typedef sequence<IntegerType1, Ones...> type;
	};

	template<typename IntegerType1, IntegerType1... Ones, typename IntegerType2,
		IntegerType2 Two, IntegerType2... Twos>
	struct remove_intersection<
		sequence<IntegerType1, Ones...>,
		sequence<IntegerType2, Two, Twos...>>
	{
		typedef typename removed_from<
			typename remove_intersection<
				sequence<IntegerType1, Ones...>,
				sequence<IntegerType2, Twos...>
			>::type,
			IntegerType2, Two
		>::type type;
	};

#pragma mark - Sort a sequence
	template<typename Sequence>
	struct sequence_max;

	template<typename IntegerType, IntegerType Max>
	struct sequence_max<sequence<IntegerType, Max>>
	{
		static constexpr IntegerType value = Max;
	};

	template<typename IntegerType, IntegerType S1, IntegerType S2,
		IntegerType... Ss>
	struct sequence_max<sequence<IntegerType, S1, S2, Ss...>>
	{
		static constexpr IntegerType value = sequence_max<
			sequence<IntegerType, (S1 > S2 ? S1 : S2), Ss...>>::value;
	};

	template<typename Sequence>
	struct sorted;

	template<typename IntegerType>
	struct sorted<sequence<IntegerType>>
	{
		typedef sequence<IntegerType> type;
	};

	template<typename IntegerType, IntegerType S1>
	struct sorted<sequence<IntegerType, S1>>
	{
		typedef sequence<IntegerType, S1> type;
	};

	template<typename IntegerType, IntegerType S1, IntegerType... Ss>
	struct sorted<sequence<IntegerType, S1, Ss...>>
	{
	private:
		typedef sequence<IntegerType, S1, Ss...> Seq;
		static constexpr IntegerType Max = sequence_max<Seq>::value;

	public:
		typedef typename combine<
			typename sorted<
				typename removed_from<Seq, IntegerType, Max>::type>::type,
			sequence<IntegerType, Max>>::type type;
	};
}

namespace unitscxx
{
	template<typename NumericType, typename Numerator, typename Denominator>
	class quantity
	{
		NumericType rawValue;

	public:
		typedef Numerator numerator;
		typedef Denominator denominator;
		typedef typename numerator::value_type unit_system;

		static_assert(std::is_same<
			typename denominator::value_type,
			typename numerator::value_type>::value,
			"quantity with incompatible unit systems");

		constexpr quantity() = default;
		constexpr quantity(const quantity&) = default;
		constexpr quantity(quantity&&) = default;
		quantity& operator=(const quantity&) = default;
		quantity& operator=(quantity&&) = default;

		explicit constexpr quantity(NumericType rawValue)
		: rawValue(rawValue)
		{
		}

		constexpr NumericType raw() const { return rawValue; }

		quantity& operator+=(quantity that)
		{
			return *this = *this + that;
		}

		quantity& operator-=(quantity that)
		{
			return *this = *this - that;
		}

		constexpr quantity operator+(quantity that) const
		{
			return quantity(raw() + that.raw());
		}

		constexpr quantity operator-(quantity that) const
		{
			return quantity(raw() - that.raw());
		}

		constexpr quantity operator+() const
		{
			return quantity(+raw());
		}

		constexpr quantity operator-() const
		{
			return quantity(-raw());
		}

		quantity& operator*=(NumericType that)
		{
			rawValue *= that;
			return *this;
		}

		quantity& operator/=(NumericType that)
		{
			rawValue /= that;
			return *this;
		}

		constexpr quantity operator*(NumericType that) const
		{
			return quantity(raw() * that);
		}

		template<typename NT, typename N, typename D>
		constexpr auto operator*(quantity<NT, N, D> that) const
		{
			typedef typename detail::combine<numerator, N>::type
				combined_numerator;

			typedef typename detail::combine<denominator, D>::type
				combined_denominator;

			typedef typename detail::remove_intersection<
				combined_numerator,
				combined_denominator>::type unsorted_numerator;

			typedef typename detail::remove_intersection<
				combined_denominator,
				combined_numerator>::type unsorted_denominator;

			typedef typename detail::sorted<unsorted_numerator>::type
				result_numerator;
			typedef typename detail::sorted<unsorted_denominator>::type
				result_denominator;

			typedef quantity<
				decltype(raw() * that.raw()),
				result_numerator,
				result_denominator> result_quantity;

			return result_quantity(raw() * that.raw());
		}

		constexpr quantity operator/(NumericType that) const
		{
			return quantity(raw() / that);
		}

		template<typename NT, typename N, typename D>
		constexpr auto operator/(quantity<NT, N, D> that) const
		{
			// flip fraction rows around for division
			typedef typename detail::combine<numerator, D>::type
				combined_numerator;

			typedef typename detail::combine<denominator, N>::type
				combined_denominator;

			typedef typename detail::remove_intersection<
				combined_numerator,
				combined_denominator>::type unsorted_numerator;

			typedef typename detail::remove_intersection<
				combined_denominator,
				combined_numerator>::type unsorted_denominator;

			typedef typename detail::sorted<unsorted_numerator>::type
				result_numerator;
			typedef typename detail::sorted<unsorted_denominator>::type
				result_denominator;

			typedef quantity<
				decltype(raw() / that.raw()),
				result_numerator,
				result_denominator> result_quantity;

			return result_quantity(raw() / that.raw());
		}
	};

	template<typename MulType, typename NT, typename N, typename D>
	constexpr auto operator*(MulType left, quantity<NT, N, D> right)
		-> typename std::enable_if<
			std::is_arithmetic<MulType>::value,
			quantity<NT, N, D>>::type
	{
		typedef typename N::value_type unit_system;
		typedef quantity<NT,
			detail::sequence<unit_system>,
			detail::sequence<unit_system>> unitless_quantity;
		return unitless_quantity(left) * right;
	}

	template<typename MulType, typename NT, typename N, typename D>
	constexpr auto operator/(MulType left, quantity<NT, N, D> right)
		-> typename std::enable_if<
			std::is_arithmetic<MulType>::value,
			quantity<NT, D, N>>::type
	{
		typedef typename N::value_type unit_system;
		typedef quantity<NT,
			detail::sequence<unit_system>,
			detail::sequence<unit_system>> unitless_quantity;
		return unitless_quantity(left) / right;
	}

	template<typename NumericType, typename UnitType, UnitType... U>
	using unit_base = quantity<NumericType,
		detail::sequence<UnitType, U...>,
		detail::sequence<UnitType>>;
}

#endif
<commit_msg>Allow implicit conversion of dimensionless quantities to NumericalType<commit_after>//
// units.hpp
// units-cxx14
//
// Copyright (c) 2016 Félix Cloutier
//
// 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.
//

#ifndef UNITS_HPP
#define UNITS_HPP

#include <cstddef>
#include <type_traits>
#include <utility>

namespace detail
{
#pragma mark - Sequence type
	// (integer_sequence only works with integers, not enums)

	template<typename T, T... S>
	struct sequence
	{
		typedef T value_type;
		static constexpr size_t size = sizeof...(S);
	};

#pragma mark - Test if a sequence contains a value
	template<typename IntegerType, IntegerType I, IntegerType... S>
	struct sequence_contains_type;

	template<typename IntegerType, IntegerType I, IntegerType S1,
		IntegerType... Ss>
	struct sequence_contains_type<IntegerType, I, S1, Ss...>
		: std::conditional<I == S1,
			std::true_type,
			typename sequence_contains_type<IntegerType, I, Ss...>::type>::type
	{
	};

	template<typename IntegerType, IntegerType I>
	struct sequence_contains_type<IntegerType, I> : std::false_type
	{
	};

	template<typename IntegerType, IntegerType I, IntegerType... Is>
	constexpr bool sequence_contains(sequence<IntegerType, Is...>)
	{
		return sequence_contains_type<IntegerType, I, Is...>::value;
	}

#pragma mark - Combine sequences
	template<typename Seq1, typename Seq2>
	struct combine;

	template<typename IntegerType, IntegerType... S1, IntegerType... S2>
	struct combine<sequence<IntegerType, S1...>, sequence<IntegerType, S2...>>
	{
		typedef sequence<IntegerType, S1..., S2...> type;
	};

	template<typename Seq1, typename Seq2>
	constexpr auto combine_sequences(Seq1, Seq2)
	{
		return typename combine<Seq1, Seq2>::type{};
	}

#pragma mark - Remove an item
	template<typename Seq, typename IntegerType, IntegerType I>
	struct left_of;

	template<typename IntegerType, IntegerType I>
	struct left_of<sequence<IntegerType>, IntegerType, I>
	{
		typedef sequence<IntegerType> type;
	};

	template<typename IntegerType, IntegerType I, IntegerType S1,
		IntegerType... Ss>
	struct left_of<sequence<IntegerType, S1, Ss...>, IntegerType, I>
	{
		typedef typename std::conditional<I == S1,
			sequence<IntegerType>,
			typename combine<
				sequence<IntegerType, S1>,
				typename left_of<
					sequence<IntegerType, Ss...>,
					IntegerType, I>::type>::type>::type type;
	};

	template<typename Seq, typename IntegerType, IntegerType I>
	struct right_of;

	template<typename IntegerType, IntegerType I>
	struct right_of<sequence<IntegerType>, IntegerType, I>
	{
		typedef sequence<IntegerType> type;
	};

	template<typename IntegerType, IntegerType I, IntegerType S1,
		IntegerType... Ss>
	struct right_of<sequence<IntegerType, S1, Ss...>, IntegerType, I>
	{
		typedef typename std::conditional<I == S1,
			sequence<IntegerType, Ss...>,
			typename right_of<sequence<IntegerType, Ss...>,
		IntegerType, I>::type>::type type;
	};

	template<typename Seq, typename IntegerType, IntegerType I>
	struct removed_from
	{
		typedef typename combine<
			typename left_of<Seq, IntegerType, I>::type,
			typename right_of<Seq, IntegerType, I>::type>::type type;
	};

#pragma mark - Remove identical terms
	template<typename Seq1, typename Seq2>
	struct remove_intersection;

	template<typename IntegerType1, IntegerType1... Ones, typename IntegerType2>
	struct remove_intersection<
		sequence<IntegerType1, Ones...>,
		sequence<IntegerType2>>
	{
		typedef sequence<IntegerType1, Ones...> type;
	};

	template<typename IntegerType1, IntegerType1... Ones, typename IntegerType2,
		IntegerType2 Two, IntegerType2... Twos>
	struct remove_intersection<
		sequence<IntegerType1, Ones...>,
		sequence<IntegerType2, Two, Twos...>>
	{
		typedef typename removed_from<
			typename remove_intersection<
				sequence<IntegerType1, Ones...>,
				sequence<IntegerType2, Twos...>
			>::type,
			IntegerType2, Two
		>::type type;
	};

#pragma mark - Sort a sequence
	template<typename Sequence>
	struct sequence_max;

	template<typename IntegerType, IntegerType Max>
	struct sequence_max<sequence<IntegerType, Max>>
	{
		static constexpr IntegerType value = Max;
	};

	template<typename IntegerType, IntegerType S1, IntegerType S2,
		IntegerType... Ss>
	struct sequence_max<sequence<IntegerType, S1, S2, Ss...>>
	{
		static constexpr IntegerType value = sequence_max<
			sequence<IntegerType, (S1 > S2 ? S1 : S2), Ss...>>::value;
	};

	template<typename Sequence>
	struct sorted;

	template<typename IntegerType>
	struct sorted<sequence<IntegerType>>
	{
		typedef sequence<IntegerType> type;
	};

	template<typename IntegerType, IntegerType S1>
	struct sorted<sequence<IntegerType, S1>>
	{
		typedef sequence<IntegerType, S1> type;
	};

	template<typename IntegerType, IntegerType S1, IntegerType... Ss>
	struct sorted<sequence<IntegerType, S1, Ss...>>
	{
	private:
		typedef sequence<IntegerType, S1, Ss...> Seq;
		static constexpr IntegerType Max = sequence_max<Seq>::value;

	public:
		typedef typename combine<
			typename sorted<
				typename removed_from<Seq, IntegerType, Max>::type>::type,
			sequence<IntegerType, Max>>::type type;
	};
}

namespace unitscxx
{
	template<typename NumericType, typename Numerator, typename Denominator>
	class quantity
	{
		NumericType rawValue;

	public:
		typedef Numerator numerator;
		typedef Denominator denominator;
		typedef typename numerator::value_type unit_system;

		static_assert(std::is_same<
			typename denominator::value_type,
			typename numerator::value_type>::value,
			"quantity with incompatible unit systems");

		constexpr quantity() = default;
		constexpr quantity(const quantity&) = default;
		constexpr quantity(quantity&&) = default;
		quantity& operator=(const quantity&) = default;
		quantity& operator=(quantity&&) = default;

		explicit constexpr quantity(NumericType rawValue)
		: rawValue(rawValue)
		{
		}

		constexpr NumericType raw() const { return rawValue; }

		quantity& operator+=(quantity that)
		{
			return *this = *this + that;
		}

		quantity& operator-=(quantity that)
		{
			return *this = *this - that;
		}

		constexpr quantity operator+(quantity that) const
		{
			return quantity(raw() + that.raw());
		}

		constexpr quantity operator-(quantity that) const
		{
			return quantity(raw() - that.raw());
		}

		constexpr quantity operator+() const
		{
			return quantity(+raw());
		}

		constexpr quantity operator-() const
		{
			return quantity(-raw());
		}

		quantity& operator*=(NumericType that)
		{
			rawValue *= that;
			return *this;
		}

		quantity& operator/=(NumericType that)
		{
			rawValue /= that;
			return *this;
		}

		constexpr quantity operator*(NumericType that) const
		{
			return quantity(raw() * that);
		}

		template<typename NT, typename N, typename D>
		constexpr auto operator*(quantity<NT, N, D> that) const
		{
			typedef typename detail::combine<numerator, N>::type
				combined_numerator;

			typedef typename detail::combine<denominator, D>::type
				combined_denominator;

			typedef typename detail::remove_intersection<
				combined_numerator,
				combined_denominator>::type unsorted_numerator;

			typedef typename detail::remove_intersection<
				combined_denominator,
				combined_numerator>::type unsorted_denominator;

			typedef typename detail::sorted<unsorted_numerator>::type
				result_numerator;
			typedef typename detail::sorted<unsorted_denominator>::type
				result_denominator;

			typedef quantity<
				decltype(raw() * that.raw()),
				result_numerator,
				result_denominator> result_quantity;

			return result_quantity(raw() * that.raw());
		}

		constexpr quantity operator/(NumericType that) const
		{
			return quantity(raw() / that);
		}

		template<typename NT, typename N, typename D>
		constexpr auto operator/(quantity<NT, N, D> that) const
		{
			// flip fraction rows around for division
			typedef typename detail::combine<numerator, D>::type
				combined_numerator;

			typedef typename detail::combine<denominator, N>::type
				combined_denominator;

			typedef typename detail::remove_intersection<
				combined_numerator,
				combined_denominator>::type unsorted_numerator;

			typedef typename detail::remove_intersection<
				combined_denominator,
				combined_numerator>::type unsorted_denominator;

			typedef typename detail::sorted<unsorted_numerator>::type
				result_numerator;
			typedef typename detail::sorted<unsorted_denominator>::type
				result_denominator;

			typedef quantity<
				decltype(raw() / that.raw()),
				result_numerator,
				result_denominator> result_quantity;

			return result_quantity(raw() / that.raw());
		}

		template<typename N = Numerator, typename D = Denominator,
			typename = typename std::enable_if<N::size == 0 && D::size == 0>::type>
		constexpr operator NumericType()
		{ return raw(); }
	};

	template<typename MulType, typename NT, typename N, typename D>
	constexpr auto operator*(MulType left, quantity<NT, N, D> right)
		-> typename std::enable_if<
			std::is_arithmetic<MulType>::value,
			quantity<NT, N, D>>::type
	{
		typedef typename N::value_type unit_system;
		typedef quantity<NT,
			detail::sequence<unit_system>,
			detail::sequence<unit_system>> unitless_quantity;
		return unitless_quantity(left) * right;
	}

	template<typename MulType, typename NT, typename N, typename D>
	constexpr auto operator/(MulType left, quantity<NT, N, D> right)
		-> typename std::enable_if<
			std::is_arithmetic<MulType>::value,
			quantity<NT, D, N>>::type
	{
		typedef typename N::value_type unit_system;
		typedef quantity<NT,
			detail::sequence<unit_system>,
			detail::sequence<unit_system>> unitless_quantity;
		return unitless_quantity(left) / right;
	}

	template<typename NumericType, typename UnitType, UnitType... U>
	using unit_base = quantity<NumericType,
		detail::sequence<UnitType, U...>,
		detail::sequence<UnitType>>;
}

#endif
<|endoftext|>


<commit_before><commit_msg>Stab-in-the-dark at fixing a top crasher.<commit_after><|endoftext|>


<commit_before>#include "ImageWidget.hpp"

namespace QArtm {

struct ImageWidget::Detail {
    bool resizing;

    bool dragging;
    QPoint dragStart, dragPos;
    QPen ribbonPen;

    Detail()
        : resizing(false),
        dragging(false),
        ribbonPen( QBrush(QColor("white")), 1, Qt::DashLine )
    {}
};

ImageWidget::ImageWidget(QWidget * parent)
    : QWidget(parent), m_detail( new Detail )
{
}

ImageWidget::~ImageWidget()
{}

void ImageWidget::paintEvent ( QPaintEvent * event )
{
    QPainter painter(this);
    painter.setRenderHint( QPainter::SmoothPixmapTransform );

    // draw the whole thing
    QSize targetSize(m_image.size());
    targetSize.scale( size(), Qt::KeepAspectRatio );

    QPoint targetOffs(0,0);

    painter.drawImage( QRect(targetOffs, targetSize), m_image, m_image.rect());
    painter.save();
    painter.scale( (float)targetSize.width() / m_image.width(),
                   (float)targetSize.height() / m_image.height());
    painter.drawPicture( targetOffs, m_overlay );
    painter.restore();

    if (m_detail->dragging) {
        painter.setPen( m_detail->ribbonPen );
        painter.drawRect( QRect( m_detail->dragStart, m_detail->dragPos ) );
    }
}

QSize ImageWidget::sizeHint () const
{
    return m_image.size();
}

void ImageWidget::setImage( const QImage& image )
{
    m_image = image;
    update();
}

void ImageWidget::resizeEvent ( QResizeEvent * event )
{
    if (m_detail->resizing) {
        QWidget::resizeEvent(event);
    } else {
        m_detail->resizing = true;
        QSize targetSize(m_image.size());
        targetSize.scale( event->size(), Qt::KeepAspectRatio );
        resize( targetSize );
        m_detail->resizing = false;
    }
}
void ImageWidget::mousePressEvent( QMouseEvent * event )
{
    m_detail->dragging = true;
    m_detail->dragStart = m_detail->dragPos = event->pos();
}

void ImageWidget::mouseReleaseEvent( QMouseEvent * event )
{
    m_detail->dragging = false;
    update();
    QSize targetSize(m_image.size());
    targetSize.scale( size(), Qt::KeepAspectRatio );
    // convert current rectangle to original image coordinates
    float sx = (float)m_image.width() / targetSize.width();
    float sy = (float)m_image.height() / targetSize.height();

    if ( (m_detail->dragStart-m_detail->dragPos).manhattanLength() < 2 )
        emit pointClicked( QPoint( m_detail->dragStart.x() * sx,
                           m_detail->dragStart.y() * sy),
                event->button());
    else
        emit rectSelected( QRect(
                QPoint( m_detail->dragStart.x() * sx,
                        m_detail->dragStart.y() * sy),
                QPoint( m_detail->dragPos.x() * sx,
                        m_detail->dragPos.y() * sy)
                ).intersected( m_image.rect() ) );
}

void ImageWidget::mouseMoveEvent( QMouseEvent * event )
{
    m_detail->dragPos = event->pos();
    update();
}

}
<commit_msg>initialize ImageWidget's size correctly<commit_after>#include "ImageWidget.hpp"

namespace QArtm {

struct ImageWidget::Detail {
    bool resizing;

    bool dragging;
    QPoint dragStart, dragPos;
    QPen ribbonPen;

    Detail()
        : resizing(false),
        dragging(false),
        ribbonPen( QBrush(QColor("white")), 1, Qt::DashLine )
    {}
};

ImageWidget::ImageWidget(QWidget * parent)
    : QWidget(parent), m_detail( new Detail )
{
}

ImageWidget::~ImageWidget()
{}

void ImageWidget::paintEvent ( QPaintEvent * event )
{
    QPainter painter(this);
    painter.setRenderHint( QPainter::SmoothPixmapTransform );

    // draw the whole thing
    QSize targetSize(m_image.size());
    targetSize.scale( size(), Qt::KeepAspectRatio );

    QPoint targetOffs(0,0);

    painter.drawImage( QRect(targetOffs, targetSize), m_image, m_image.rect());
    painter.save();
    painter.scale( (float)targetSize.width() / m_image.width(),
                   (float)targetSize.height() / m_image.height());
    painter.drawPicture( targetOffs, m_overlay );
    painter.restore();

    if (m_detail->dragging) {
        painter.setPen( m_detail->ribbonPen );
        painter.drawRect( QRect( m_detail->dragStart, m_detail->dragPos ) );
    }
}

QSize ImageWidget::sizeHint () const
{
    return m_image.size();
}

void ImageWidget::setImage( const QImage& image )
{
    QImage old = m_image;
    m_image = image;
    if (old.size() != image.size()) {
        QResizeEvent ev( size(), size() );
        event(&ev);
    }
    update();
}

void ImageWidget::resizeEvent ( QResizeEvent * event )
{
    if (m_detail->resizing) {
        QWidget::resizeEvent(event);
    } else {
        m_detail->resizing = true;
        QSize targetSize(m_image.size());
        targetSize.scale( event->size(), Qt::KeepAspectRatio );
        resize( targetSize );
        m_detail->resizing = false;
    }
}
void ImageWidget::mousePressEvent( QMouseEvent * event )
{
    m_detail->dragging = true;
    m_detail->dragStart = m_detail->dragPos = event->pos();
}

void ImageWidget::mouseReleaseEvent( QMouseEvent * event )
{
    m_detail->dragging = false;
    update();
    QSize targetSize(m_image.size());
    targetSize.scale( size(), Qt::KeepAspectRatio );
    // convert current rectangle to original image coordinates
    float sx = (float)m_image.width() / targetSize.width();
    float sy = (float)m_image.height() / targetSize.height();

    if ( (m_detail->dragStart-m_detail->dragPos).manhattanLength() < 2 )
        emit pointClicked( QPoint( m_detail->dragStart.x() * sx,
                           m_detail->dragStart.y() * sy),
                event->button());
    else
        emit rectSelected( QRect(
                QPoint( m_detail->dragStart.x() * sx,
                        m_detail->dragStart.y() * sy),
                QPoint( m_detail->dragPos.x() * sx,
                        m_detail->dragPos.y() * sy)
                ).intersected( m_image.rect() ) );
}

void ImageWidget::mouseMoveEvent( QMouseEvent * event )
{
    m_detail->dragPos = event->pos();
    update();
}

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: xmlreg.hxx,v $
 *
 *  $Revision: 1.1 $
 *
 *  last change: $Author: thb $ $Date: 2001-07-24 17:06:06 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _XMLOFF_XMLREG_HXX
#define _XMLOFF_XMLREG_HXX

#ifndef _RTL_REF_HXX_
#include <rtl/ref.hxx>
#endif

#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif

/**
 *  Register subset of UNO services from XML Office library. This is necessary when
 *  linking against the static "xol.lib".
 *
 *  @return returns sal_False if at least one component could not be registered.
 */
sal_Bool XMLRegisterServices( ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > ); /// [all] ServiceProvider to register in.

#endif  //  _XMLOFF_XMLREG_HXX

<commit_msg>INTEGRATION: CWS ooo19126 (1.1.636); FILE MERGED 2005/09/05 14:38:15 rt 1.1.636.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: xmlreg.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 13:19:20 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _XMLOFF_XMLREG_HXX
#define _XMLOFF_XMLREG_HXX

#ifndef _RTL_REF_HXX_
#include <rtl/ref.hxx>
#endif

#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif

/**
 *  Register subset of UNO services from XML Office library. This is necessary when
 *  linking against the static "xol.lib".
 *
 *  @return returns sal_False if at least one component could not be registered.
 */
sal_Bool XMLRegisterServices( ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > ); /// [all] ServiceProvider to register in.

#endif  //  _XMLOFF_XMLREG_HXX

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "tst_blackboxandroid.h"

#include "../shared.h"
#include <tools/profile.h>
#include <tools/qttools.h>

#include <QtCore/qjsondocument.h>
#include <QtCore/qset.h>
#include <QtCore/qtemporarydir.h>

using qbs::Profile;

QMap<QString, QString> TestBlackboxAndroid::findAndroid(int *status, const QString &profile)
{
    QTemporaryDir temp;
    QDir::setCurrent(testDataDir + "/find");
    QbsRunParameters params = QStringList({"-f", "find-android.qbs", "qbs.architecture:x86"});
    params.profile = profile;
    params.buildDirectory = temp.path();
    const int res = runQbs(params);
    if (status)
        *status = res;
    QFile file(temp.path() + "/" + relativeProductBuildDir("find-android")
               + "/android.json");
    if (!file.open(QIODevice::ReadOnly))
        return QMap<QString, QString> { };
    const auto tools = QJsonDocument::fromJson(file.readAll()).toVariant().toMap();
    return QMap<QString, QString> {
        {"sdk", QDir::fromNativeSeparators(tools["sdk"].toString())},
        {"sdk-build-tools-dx", QDir::fromNativeSeparators(tools["sdk-build-tools-dx"].toString())},
        {"ndk", QDir::fromNativeSeparators(tools["ndk"].toString())},
        {"ndk-samples", QDir::fromNativeSeparators(tools["ndk-samples"].toString())},
    };
}

TestBlackboxAndroid::TestBlackboxAndroid()
    : TestBlackboxBase(SRCDIR "/testdata-android", "blackbox-android")
{
}

void TestBlackboxAndroid::android()
{
    QFETCH(const QString, projectDir);
    QFETCH(const QStringList, productNames);
    QFETCH(const QList<QByteArrayList>, expectedFilesLists);

    const SettingsPtr s = settings();
    Profile p(profileName(), s.get());
    int status;
    const auto androidPaths = findAndroid(&status, p.name());
    QCOMPARE(status, 0);

    const auto sdkPath = androidPaths["sdk"];
    if (sdkPath.isEmpty())
        QSKIP("Android SDK is not installed");

    const auto ndkPath = androidPaths["ndk"];
    if (ndkPath.isEmpty() && projectDir != "no-native")
        QSKIP("Android NDK is not installed");

    const auto ndkSamplesPath = androidPaths["ndk-samples"];
    static const QStringList ndkSamplesDirs = QStringList() << "teapot" << "no-native";
    if (!ndkPath.isEmpty() && !QFileInfo(ndkSamplesPath).isDir()
            && ndkSamplesDirs.contains(projectDir))
        QSKIP("NDK samples directory not present");

    QDir::setCurrent(testDataDir + "/" + projectDir);

    static const QStringList configNames { "debug", "release" };
    for (const QString &configName : configNames) {
        auto currentExpectedFilesLists = expectedFilesLists;
        QbsRunParameters params(QStringList { "--command-echo-mode", "command-line",
                                              "modules.Android.ndk.platform:android-21",
                                              "config:" + configName });
        params.profile = p.name();
        QCOMPARE(runQbs(params), 0);
        for (const QString &productName : qAsConst(productNames)) {
            QVERIFY(m_qbsStdout.contains(productName.toLocal8Bit() + ".apk"));
            const QString apkFilePath = relativeProductBuildDir(productName, configName)
                    + '/' + productName + ".apk";
            QVERIFY2(regularFileExists(apkFilePath), qPrintable(apkFilePath));
            const QString jarFilePath = findExecutable(QStringList("jar"));
            QVERIFY(!jarFilePath.isEmpty());
            QProcess jar;
            jar.start(jarFilePath, QStringList() << "-tf" << apkFilePath);
            QVERIFY2(jar.waitForStarted(), qPrintable(jar.errorString()));
            QVERIFY2(jar.waitForFinished(), qPrintable(jar.errorString()));
            QVERIFY2(jar.exitCode() == 0, qPrintable(jar.readAllStandardError().constData()));
            QByteArrayList actualFiles = jar.readAllStandardOutput().trimmed().split('\n');
            QByteArrayList missingExpectedFiles;
            QByteArrayList expectedFiles = currentExpectedFilesLists.takeFirst();
            for (const QByteArray &expectedFile : expectedFiles) {
                if (expectedFile.endsWith("/gdbserver") && configName == "release")
                    continue;
                auto it = std::find(actualFiles.begin(), actualFiles.end(), expectedFile);
                if (it != actualFiles.end()) {
                    actualFiles.erase(it);
                    continue;
                }
                missingExpectedFiles << expectedFile;
            }
            if (!missingExpectedFiles.empty())
                QFAIL(QByteArray("missing expected files:\n") + missingExpectedFiles.join('\n'));
            if (!actualFiles.empty()) {
                QByteArray msg = "unexpected files encountered:\n" + actualFiles.join('\n');
                auto it = std::find_if(std::begin(actualFiles), std::end(actualFiles),
                                       [](const QByteArray &f) {
                    return f.endsWith(".so");
                });
                if (it == std::end(actualFiles))
                    QWARN(msg);
                else
                    QFAIL(msg);
            }
        }

        if (projectDir == "multiple-libs-per-apk") {
            const auto dxPath = androidPaths["sdk-build-tools-dx"];
            QVERIFY(!dxPath.isEmpty());
            const auto lines = m_qbsStdout.split('\n');
            const auto it = std::find_if(lines.cbegin(), lines.cend(), [&](const QByteArray &line) {
                return !line.isEmpty() && line.startsWith(dxPath.toUtf8());
            });
            QVERIFY2(it != lines.cend(), qPrintable(m_qbsStdout.constData()));
            const auto line = *it;
            QVERIFY2(line.contains("lib3.jar"), qPrintable(line.constData()));
            QVERIFY2(!line.contains("lib4.jar"), qPrintable(line.constData()));
            QVERIFY2(line.contains("lib5.jar"), qPrintable(line.constData()));
            QVERIFY2(line.contains("lib6.jar"), qPrintable(line.constData()));
            QVERIFY2(!line.contains("lib7.jar"), qPrintable(line.constData()));
            QVERIFY2(line.contains("lib8.jar"), qPrintable(line.constData()));
        }
    }
}

void TestBlackboxAndroid::android_data()
{
    const SettingsPtr s = settings();
    const Profile p(profileName(), s.get());
    QStringList archsStringList = p.value(QLatin1String("qbs.architectures")).toStringList();
    if (archsStringList.empty())
        archsStringList << QStringLiteral("armv7a"); // must match default in common.qbs
    QByteArrayList archs;
    std::transform(archsStringList.begin(), archsStringList.end(), std::back_inserter(archs),
                   [] (const QString &s) {
        return s.toUtf8().replace("armv7a", "armeabi-v7a")
                .replace("armv5te", "armeabi")
                .replace("arm64", "arm64-v8a");
    });

    auto expandArchs = [] (const QByteArrayList &archs, const QByteArrayList &lst) {
        const QByteArray &archPlaceHolder = "${ARCH}";
        QByteArrayList result;
        for (const QByteArray &entry : lst) {
            if (entry.contains(archPlaceHolder)) {
                for (const QByteArray &arch : qAsConst(archs))
                    result << QByteArray(entry).replace(archPlaceHolder, arch);
            } else {
                result << entry;
            }
        }
        return result;
    };

    const QByteArrayList commonFiles = expandArchs(archs, {
        "AndroidManifest.xml", "META-INF/ANDROIDD.RSA", "META-INF/ANDROIDD.SF",
        "META-INF/MANIFEST.MF", "classes.dex", "resources.arsc"
    });

    QTest::addColumn<QString>("projectDir");
    QTest::addColumn<QStringList>("productNames");
    QTest::addColumn<QList<QByteArrayList>>("expectedFilesLists");
    QTest::newRow("teapot")
            << "teapot" << QStringList("com.sample.teapot")
            << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
                       "assets/Shaders/ShaderPlain.fsh",
                       "assets/Shaders/VS_ShaderPlain.vsh",
                       "lib/${ARCH}/gdbserver",
                       "lib/${ARCH}/libgnustl_shared.so",
                       "lib/${ARCH}/libTeapotNativeActivity.so",
                       "res/layout/widgets.xml"}));
    QTest::newRow("no native")
            << "no-native"
            << QStringList("com.example.android.basicmediadecoder")
            << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
                       "res/drawable-hdpi-v4/ic_action_play_disabled.png",
                       "res/drawable-hdpi-v4/ic_action_play.png",
                       "res/drawable-hdpi-v4/ic_launcher.png",
                       "res/drawable-hdpi-v4/tile.9.png",
                       "res/drawable-mdpi-v4/ic_action_play_disabled.png",
                       "res/drawable-mdpi-v4/ic_action_play.png",
                       "res/drawable-mdpi-v4/ic_launcher.png",
                       "res/drawable/selector_play.xml",
                       "res/drawable-xhdpi-v4/ic_action_play_disabled.png",
                       "res/drawable-xhdpi-v4/ic_action_play.png",
                       "res/drawable-xhdpi-v4/ic_launcher.png",
                       "res/drawable-xxhdpi-v4/ic_launcher.png",
                       "res/layout/sample_main.xml",
                       "res/menu/action_menu.xml",
                       "res/menu-v11/action_menu.xml",
                       "res/raw/vid_bigbuckbunny.mp4"}));
    QTest::newRow("multiple libs")
            << "multiple-libs-per-apk"
            << QStringList("twolibs")
            << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
                       "lib/${ARCH}/gdbserver",
                       "lib/${ARCH}/liblib1.so",
                       "lib/${ARCH}/liblib2.so",
                       "lib/${ARCH}/libstlport_shared.so"}));
    QByteArrayList expectedFiles1 = (commonFiles
            + expandArchs(QByteArrayList{"mips", "x86"}, {
                              "lib/${ARCH}/gdbserver",
                              "lib/${ARCH}/libp1lib1.so",
                              "lib/${ARCH}/libstlport_shared.so"})
            + expandArchs(QByteArrayList{archs}, {
                              "lib/${ARCH}/gdbserver",
                              "lib/${ARCH}/libp1lib2.so",
                              "lib/${ARCH}/libstlport_shared.so"})).toSet().toList();
    QByteArrayList expectedFiles2 = commonFiles + expandArchs(archs, {
                       "lib/${ARCH}/gdbserver",
                       "lib/${ARCH}/libp2lib1.so",
                       "lib/${ARCH}/libp2lib2.so",
                       "lib/${ARCH}/libstlport_shared.so"});
    expectedFiles2.removeOne("resources.arsc");
    QTest::newRow("multiple apks")
            << "multiple-apks-per-project"
            << (QStringList() << "twolibs1" << "twolibs2")
            << QList<QByteArrayList>{expectedFiles1, expectedFiles2};
}

QTEST_MAIN(TestBlackboxAndroid)
<commit_msg>Fix build<commit_after>/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "tst_blackboxandroid.h"

#include "../shared.h"
#include <tools/profile.h>
#include <tools/qttools.h>

#include <QtCore/qjsondocument.h>
#include <QtCore/qset.h>
#include <QtCore/qtemporarydir.h>

using qbs::Profile;

QMap<QString, QString> TestBlackboxAndroid::findAndroid(int *status, const QString &profile)
{
    QTemporaryDir temp;
    QDir::setCurrent(testDataDir + "/find");
    QbsRunParameters params = QStringList({"-f", "find-android.qbs", "qbs.architecture:x86"});
    params.profile = profile;
    params.buildDirectory = temp.path();
    const int res = runQbs(params);
    if (status)
        *status = res;
    QFile file(temp.path() + "/" + relativeProductBuildDir("find-android")
               + "/android.json");
    if (!file.open(QIODevice::ReadOnly))
        return QMap<QString, QString> { };
    const auto tools = QJsonDocument::fromJson(file.readAll()).toVariant().toMap();
    return QMap<QString, QString> {
        {"sdk", QDir::fromNativeSeparators(tools["sdk"].toString())},
        {"sdk-build-tools-dx", QDir::fromNativeSeparators(tools["sdk-build-tools-dx"].toString())},
        {"ndk", QDir::fromNativeSeparators(tools["ndk"].toString())},
        {"ndk-samples", QDir::fromNativeSeparators(tools["ndk-samples"].toString())},
    };
}

TestBlackboxAndroid::TestBlackboxAndroid()
    : TestBlackboxBase(SRCDIR "/testdata-android", "blackbox-android")
{
}

void TestBlackboxAndroid::android()
{
    QFETCH(QString, projectDir);
    QFETCH(QStringList, productNames);
    QFETCH(QList<QByteArrayList>, expectedFilesLists);

    const SettingsPtr s = settings();
    Profile p(profileName(), s.get());
    int status;
    const auto androidPaths = findAndroid(&status, p.name());
    QCOMPARE(status, 0);

    const auto sdkPath = androidPaths["sdk"];
    if (sdkPath.isEmpty())
        QSKIP("Android SDK is not installed");

    const auto ndkPath = androidPaths["ndk"];
    if (ndkPath.isEmpty() && projectDir != "no-native")
        QSKIP("Android NDK is not installed");

    const auto ndkSamplesPath = androidPaths["ndk-samples"];
    static const QStringList ndkSamplesDirs = QStringList() << "teapot" << "no-native";
    if (!ndkPath.isEmpty() && !QFileInfo(ndkSamplesPath).isDir()
            && ndkSamplesDirs.contains(projectDir))
        QSKIP("NDK samples directory not present");

    QDir::setCurrent(testDataDir + "/" + projectDir);

    static const QStringList configNames { "debug", "release" };
    for (const QString &configName : configNames) {
        auto currentExpectedFilesLists = expectedFilesLists;
        QbsRunParameters params(QStringList { "--command-echo-mode", "command-line",
                                              "modules.Android.ndk.platform:android-21",
                                              "config:" + configName });
        params.profile = p.name();
        QCOMPARE(runQbs(params), 0);
        for (const QString &productName : qAsConst(productNames)) {
            QVERIFY(m_qbsStdout.contains(productName.toLocal8Bit() + ".apk"));
            const QString apkFilePath = relativeProductBuildDir(productName, configName)
                    + '/' + productName + ".apk";
            QVERIFY2(regularFileExists(apkFilePath), qPrintable(apkFilePath));
            const QString jarFilePath = findExecutable(QStringList("jar"));
            QVERIFY(!jarFilePath.isEmpty());
            QProcess jar;
            jar.start(jarFilePath, QStringList() << "-tf" << apkFilePath);
            QVERIFY2(jar.waitForStarted(), qPrintable(jar.errorString()));
            QVERIFY2(jar.waitForFinished(), qPrintable(jar.errorString()));
            QVERIFY2(jar.exitCode() == 0, qPrintable(jar.readAllStandardError().constData()));
            QByteArrayList actualFiles = jar.readAllStandardOutput().trimmed().split('\n');
            QByteArrayList missingExpectedFiles;
            QByteArrayList expectedFiles = currentExpectedFilesLists.takeFirst();
            for (const QByteArray &expectedFile : expectedFiles) {
                if (expectedFile.endsWith("/gdbserver") && configName == "release")
                    continue;
                auto it = std::find(actualFiles.begin(), actualFiles.end(), expectedFile);
                if (it != actualFiles.end()) {
                    actualFiles.erase(it);
                    continue;
                }
                missingExpectedFiles << expectedFile;
            }
            if (!missingExpectedFiles.empty())
                QFAIL(QByteArray("missing expected files:\n") + missingExpectedFiles.join('\n'));
            if (!actualFiles.empty()) {
                QByteArray msg = "unexpected files encountered:\n" + actualFiles.join('\n');
                auto it = std::find_if(std::begin(actualFiles), std::end(actualFiles),
                                       [](const QByteArray &f) {
                    return f.endsWith(".so");
                });
                if (it == std::end(actualFiles))
                    QWARN(msg);
                else
                    QFAIL(msg);
            }
        }

        if (projectDir == "multiple-libs-per-apk") {
            const auto dxPath = androidPaths["sdk-build-tools-dx"];
            QVERIFY(!dxPath.isEmpty());
            const auto lines = m_qbsStdout.split('\n');
            const auto it = std::find_if(lines.cbegin(), lines.cend(), [&](const QByteArray &line) {
                return !line.isEmpty() && line.startsWith(dxPath.toUtf8());
            });
            QVERIFY2(it != lines.cend(), qPrintable(m_qbsStdout.constData()));
            const auto line = *it;
            QVERIFY2(line.contains("lib3.jar"), qPrintable(line.constData()));
            QVERIFY2(!line.contains("lib4.jar"), qPrintable(line.constData()));
            QVERIFY2(line.contains("lib5.jar"), qPrintable(line.constData()));
            QVERIFY2(line.contains("lib6.jar"), qPrintable(line.constData()));
            QVERIFY2(!line.contains("lib7.jar"), qPrintable(line.constData()));
            QVERIFY2(line.contains("lib8.jar"), qPrintable(line.constData()));
        }
    }
}

void TestBlackboxAndroid::android_data()
{
    const SettingsPtr s = settings();
    const Profile p(profileName(), s.get());
    QStringList archsStringList = p.value(QLatin1String("qbs.architectures")).toStringList();
    if (archsStringList.empty())
        archsStringList << QStringLiteral("armv7a"); // must match default in common.qbs
    QByteArrayList archs;
    std::transform(archsStringList.begin(), archsStringList.end(), std::back_inserter(archs),
                   [] (const QString &s) {
        return s.toUtf8().replace("armv7a", "armeabi-v7a")
                .replace("armv5te", "armeabi")
                .replace("arm64", "arm64-v8a");
    });

    auto expandArchs = [] (const QByteArrayList &archs, const QByteArrayList &lst) {
        const QByteArray &archPlaceHolder = "${ARCH}";
        QByteArrayList result;
        for (const QByteArray &entry : lst) {
            if (entry.contains(archPlaceHolder)) {
                for (const QByteArray &arch : qAsConst(archs))
                    result << QByteArray(entry).replace(archPlaceHolder, arch);
            } else {
                result << entry;
            }
        }
        return result;
    };

    const QByteArrayList commonFiles = expandArchs(archs, {
        "AndroidManifest.xml", "META-INF/ANDROIDD.RSA", "META-INF/ANDROIDD.SF",
        "META-INF/MANIFEST.MF", "classes.dex", "resources.arsc"
    });

    QTest::addColumn<QString>("projectDir");
    QTest::addColumn<QStringList>("productNames");
    QTest::addColumn<QList<QByteArrayList>>("expectedFilesLists");
    QTest::newRow("teapot")
            << "teapot" << QStringList("com.sample.teapot")
            << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
                       "assets/Shaders/ShaderPlain.fsh",
                       "assets/Shaders/VS_ShaderPlain.vsh",
                       "lib/${ARCH}/gdbserver",
                       "lib/${ARCH}/libgnustl_shared.so",
                       "lib/${ARCH}/libTeapotNativeActivity.so",
                       "res/layout/widgets.xml"}));
    QTest::newRow("no native")
            << "no-native"
            << QStringList("com.example.android.basicmediadecoder")
            << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
                       "res/drawable-hdpi-v4/ic_action_play_disabled.png",
                       "res/drawable-hdpi-v4/ic_action_play.png",
                       "res/drawable-hdpi-v4/ic_launcher.png",
                       "res/drawable-hdpi-v4/tile.9.png",
                       "res/drawable-mdpi-v4/ic_action_play_disabled.png",
                       "res/drawable-mdpi-v4/ic_action_play.png",
                       "res/drawable-mdpi-v4/ic_launcher.png",
                       "res/drawable/selector_play.xml",
                       "res/drawable-xhdpi-v4/ic_action_play_disabled.png",
                       "res/drawable-xhdpi-v4/ic_action_play.png",
                       "res/drawable-xhdpi-v4/ic_launcher.png",
                       "res/drawable-xxhdpi-v4/ic_launcher.png",
                       "res/layout/sample_main.xml",
                       "res/menu/action_menu.xml",
                       "res/menu-v11/action_menu.xml",
                       "res/raw/vid_bigbuckbunny.mp4"}));
    QTest::newRow("multiple libs")
            << "multiple-libs-per-apk"
            << QStringList("twolibs")
            << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
                       "lib/${ARCH}/gdbserver",
                       "lib/${ARCH}/liblib1.so",
                       "lib/${ARCH}/liblib2.so",
                       "lib/${ARCH}/libstlport_shared.so"}));
    QByteArrayList expectedFiles1 = (commonFiles
            + expandArchs(QByteArrayList{"mips", "x86"}, {
                              "lib/${ARCH}/gdbserver",
                              "lib/${ARCH}/libp1lib1.so",
                              "lib/${ARCH}/libstlport_shared.so"})
            + expandArchs(QByteArrayList{archs}, {
                              "lib/${ARCH}/gdbserver",
                              "lib/${ARCH}/libp1lib2.so",
                              "lib/${ARCH}/libstlport_shared.so"})).toSet().toList();
    QByteArrayList expectedFiles2 = commonFiles + expandArchs(archs, {
                       "lib/${ARCH}/gdbserver",
                       "lib/${ARCH}/libp2lib1.so",
                       "lib/${ARCH}/libp2lib2.so",
                       "lib/${ARCH}/libstlport_shared.so"});
    expectedFiles2.removeOne("resources.arsc");
    QTest::newRow("multiple apks")
            << "multiple-apks-per-project"
            << (QStringList() << "twolibs1" << "twolibs2")
            << QList<QByteArrayList>{expectedFiles1, expectedFiles2};
}

QTEST_MAIN(TestBlackboxAndroid)
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Linguist of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#if CHECK_SIMTEXTH
#include "../shared/simtexth.h"
#endif

#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QByteArray>

#include <QtTest/QtTest>

class tst_lupdate : public QObject
{
    Q_OBJECT
public:
    tst_lupdate();

private slots:
    void good_data();
    void good();
    void commandline_data();
    void commandline();
#if CHECK_SIMTEXTH
    void simtexth();
    void simtexth_data();
#endif

private:
    QString m_cmdLupdate;
    QString m_basePath;

    void doCompare(const QStringList &actual, const QString &expectedFn, bool err);
    void doCompare(const QString &actualFn, const QString &expectedFn, bool err);
};

tst_lupdate::tst_lupdate()
{
    QString binPath = QLibraryInfo::location(QLibraryInfo::BinariesPath);
    m_cmdLupdate = binPath + QLatin1String("/lupdate");
    m_basePath = QDir::currentPath() + QLatin1String("/testdata/");
}

static bool prepareMatch(const QString &expect, QString *tmpl, int *require, int *accept)
{
    if (expect.startsWith(QLatin1Char('\\'))) {
        *tmpl = expect.mid(1);
        *require = *accept = 1;
    } else if (expect.startsWith(QLatin1Char('?'))) {
        *tmpl = expect.mid(1);
        *require = 0;
        *accept = 1;
    } else if (expect.startsWith(QLatin1Char('*'))) {
        *tmpl = expect.mid(1);
        *require = 0;
        *accept = INT_MAX;
    } else if (expect.startsWith(QLatin1Char('+'))) {
        *tmpl = expect.mid(1);
        *require = 1;
        *accept = INT_MAX;
    } else if (expect.startsWith(QLatin1Char('{'))) {
        int brc = expect.indexOf(QLatin1Char('}'), 1);
        if (brc < 0)
            return false;
        *tmpl = expect.mid(brc + 1);
        QString sub = expect.mid(1, brc - 1);
        int com = sub.indexOf(QLatin1Char(','));
        bool ok;
        if (com < 0) {
            *require = *accept = sub.toInt(&ok);
            return ok;
        } else {
            *require = sub.left(com).toInt();
            *accept = sub.mid(com + 1).toInt(&ok);
            if (!ok)
                *accept = INT_MAX;
            return *accept >= *require;
        }
    } else {
        *tmpl = expect;
        *require = *accept = 1;
    }
    return true;
}

void tst_lupdate::doCompare(const QStringList &actual, const QString &expectedFn, bool err)
{
    QFile file(expectedFn);
    QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(expectedFn));
    QStringList expected = QString(file.readAll()).split('\n');

    int ei = 0, ai = 0, em = expected.size(), am = actual.size();
    int oei = 0, oai = 0, oem = em, oam = am;
    int require = 0, accept = 0;
    QString tmpl;
    forever {
        if (!accept) {
            oei = ei, oai = ai;
            if (ei == em) {
                if (ai == am)
                    return;
                break;
            }
            if (!prepareMatch(expected.at(ei++), &tmpl, &require, &accept))
                QFAIL(qPrintable(QString("Malformed expected %1 at %3:%2")
                                 .arg(err ? "output" : "result").arg(ei).arg(expectedFn)));
        }
        if (ai == am) {
            if (require <= 0) {
                accept = 0;
                continue;
            }
            break;
        }
        if (err ? !QRegExp(tmpl).exactMatch(actual.at(ai)) : (actual.at(ai) != tmpl)) {
            if (require <= 0) {
                accept = 0;
                continue;
            }
            ei--;
            require = accept = 0;
            forever {
                if (!accept) {
                    oem = em, oam = am;
                    if (ei == em)
                        break;
                    if (!prepareMatch(expected.at(--em), &tmpl, &require, &accept))
                        QFAIL(qPrintable(QString("Malformed expected %1 at %3:%2")
                                         .arg(err ? "output" : "result")
                                         .arg(em + 1).arg(expectedFn)));
                }
                if (ai == am || (err ? !QRegExp(tmpl).exactMatch(actual.at(am - 1)) :
                                       (actual.at(am - 1) != tmpl))) {
                    if (require <= 0) {
                        accept = 0;
                        continue;
                    }
                    break;
                }
                accept--;
                require--;
                am--;
            }
            break;
        }
        accept--;
        require--;
        ai++;
    }
    QByteArray diff;
    for (int j = qMax(0, oai - 3); j < oai; j++)
        diff += actual.at(j) + '\n';
    diff += "<<<<<<< got\n";
    for (int j = oai; j < oam; j++) {
        diff += actual.at(j) + '\n';
        if (j >= oai + 5) {
            diff += "...\n";
            break;
        }
    }
    diff += "=========\n";
    for (int j = oei; j < oem; j++) {
        diff += expected.at(j) + '\n';
        if (j >= oei + 5) {
            diff += "...\n";
            break;
        }
    }
    diff += ">>>>>>> expected\n";
    for (int j = oam; j < qMin(oam + 3, actual.size()); j++)
        diff += actual.at(j) + '\n';
    QFAIL(qPrintable((err ? "Output for " : "Result for ") + expectedFn + " does not meet expectations:\n" + diff));
}

void tst_lupdate::doCompare(const QString &actualFn, const QString &expectedFn, bool err)
{
    QFile afile(actualFn);
    QVERIFY2(afile.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(actualFn));
    QStringList actual = QString(afile.readAll()).split('\n');

    doCompare(actual, expectedFn, err);
}

void tst_lupdate::good_data()
{
    QTest::addColumn<QString>("directory");

    QDir parsingDir(m_basePath + "good");
    QStringList dirs = parsingDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);

#ifndef Q_OS_WIN
    dirs.removeAll(QLatin1String("backslashes"));
#endif

    foreach (const QString &dir, dirs)
        QTest::newRow(dir.toLocal8Bit()) << dir;
}

void tst_lupdate::good()
{
    QFETCH(QString, directory);

    QString dir = m_basePath + "good/" + directory;

    qDebug() << "Checking...";

    QString generatedtsfile(dir + QLatin1String("/project.ts"));

    // look for a command
    QString lupdatecmd;
    QFile file(dir + "/lupdatecmd");
    if (file.exists()) {
        QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(file.fileName()));
        while (!file.atEnd()) {
            QByteArray cmdstring = file.readLine().simplified();
            if (cmdstring.startsWith('#'))
                continue;
            if (cmdstring.startsWith("lupdate")) {
                cmdstring.remove(0, 8);
                lupdatecmd.append(cmdstring);
                break;
            } else if (cmdstring.startsWith("TRANSLATION:")) {
                cmdstring.remove(0, 12);
                generatedtsfile = dir + QLatin1Char('/') + cmdstring.trimmed();
            }
        }
        file.close();
    }

    QFile::remove(generatedtsfile);
    QString beforetsfile = generatedtsfile + QLatin1String(".before");
    if (QFile::exists(beforetsfile))
        QVERIFY2(QFile::copy(beforetsfile, generatedtsfile), qPrintable(beforetsfile));

    if (lupdatecmd.isEmpty())
        lupdatecmd = QLatin1String("project.pro");
    lupdatecmd.prepend("-silent ");

    QProcess proc;
    proc.setWorkingDirectory(dir);
    proc.setProcessChannelMode(QProcess::MergedChannels);
    proc.start(m_cmdLupdate + ' ' + lupdatecmd, QIODevice::ReadWrite | QIODevice::Text);
    QVERIFY2(proc.waitForFinished(5000), qPrintable(lupdatecmd));
    QByteArray output = proc.readAll();
    QVERIFY2(proc.exitStatus() == QProcess::NormalExit,
             "\"lupdate " + lupdatecmd.toLatin1() + "\" crashed\n" + output);
    QVERIFY2(!proc.exitCode(),
             "\"lupdate " + lupdatecmd.toLatin1() + "\" exited with code " +
             QByteArray::number(proc.exitCode()) + "\n" + output);

    // If the file expectedoutput.txt exists, compare the
    // console output with the content of that file
    QFile outfile(dir + "/expectedoutput.txt");
    if (outfile.exists()) {
        QStringList errslist = QString::fromLatin1(output).split(QLatin1Char('\n'));
        doCompare(errslist, outfile.fileName(), true);
        if (QTest::currentTestFailed())
            return;
    }

    QString expectedFile = generatedtsfile + QLatin1String(".result");
    doCompare(generatedtsfile, expectedFile, false);
}

void tst_lupdate::commandline_data()
{
    QTest::addColumn<QString>("currentPath");
    QTest::addColumn<QString>("commandline");
    QTest::addColumn<QString>("generatedtsfile");
    QTest::addColumn<QString>("expectedtsfile");

    QTest::newRow("Recursive scan") << QString("recursivescan")
       << QString(". -ts foo.ts") << QString("foo.ts") << QString("foo.ts.result");
    QTest::newRow("Deep path argument") << QString("recursivescan")
       << QString("sub/finddialog.cpp -ts bar.ts") << QString("bar.ts") << QString("bar.ts.result");
}

void tst_lupdate::commandline()
{
    QFETCH(QString, currentPath);
    QFETCH(QString, commandline);
    QFETCH(QString, generatedtsfile);
    QFETCH(QString, expectedtsfile);

    QString generated =
        m_basePath + currentPath + QLatin1Char('/') + generatedtsfile;
    QFile gen(generated);
    if (gen.exists())
        QVERIFY(gen.remove());
    QProcess proc;
    proc.setWorkingDirectory(m_basePath + currentPath);
    proc.setProcessChannelMode(QProcess::MergedChannels);
    proc.start(m_cmdLupdate + " -silent " + commandline, QIODevice::ReadWrite | QIODevice::Text);
    QVERIFY2(proc.waitForFinished(5000), qPrintable(commandline));
    QVERIFY2(proc.exitStatus() == QProcess::NormalExit,
             "\"lupdate -silent " + commandline.toLatin1() + "\" crashed\n" + proc.readAll());
    QVERIFY2(!proc.exitCode(),
             "\"lupdate -silent " + commandline.toLatin1() + "\" exited with code " +
             QByteArray::number(proc.exitCode()) + "\n" + proc.readAll());

    doCompare(generated, m_basePath + currentPath + QLatin1Char('/') + expectedtsfile, false);
}

#if CHECK_SIMTEXTH
void tst_lupdate::simtexth()
{
    QFETCH(QString, one);
    QFETCH(QString, two);
    QFETCH(int, expected);

    int measured = getSimilarityScore(one, two.toLatin1());
    QCOMPARE(measured, expected);
}

void tst_lupdate::simtexth_data()
{
    using namespace QTest;

    addColumn<QString>("one");
    addColumn<QString>("two");
    addColumn<int>("expected");

    newRow("00") << "" << "" << 1024;
    newRow("01") << "a" << "a" << 1024;
    newRow("02") << "ab" << "ab" << 1024;
    newRow("03") << "abc" << "abc" << 1024;
    newRow("04") << "abcd" << "abcd" << 1024;
}
#endif

QTEST_MAIN(tst_lupdate)
#include "tst_lupdate.moc"
<commit_msg>support multiple output files<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Linguist of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#if CHECK_SIMTEXTH
#include "../shared/simtexth.h"
#endif

#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QByteArray>

#include <QtTest/QtTest>

class tst_lupdate : public QObject
{
    Q_OBJECT
public:
    tst_lupdate();

private slots:
    void good_data();
    void good();
    void commandline_data();
    void commandline();
#if CHECK_SIMTEXTH
    void simtexth();
    void simtexth_data();
#endif

private:
    QString m_cmdLupdate;
    QString m_basePath;

    void doCompare(const QStringList &actual, const QString &expectedFn, bool err);
    void doCompare(const QString &actualFn, const QString &expectedFn, bool err);
};

tst_lupdate::tst_lupdate()
{
    QString binPath = QLibraryInfo::location(QLibraryInfo::BinariesPath);
    m_cmdLupdate = binPath + QLatin1String("/lupdate");
    m_basePath = QDir::currentPath() + QLatin1String("/testdata/");
}

static bool prepareMatch(const QString &expect, QString *tmpl, int *require, int *accept)
{
    if (expect.startsWith(QLatin1Char('\\'))) {
        *tmpl = expect.mid(1);
        *require = *accept = 1;
    } else if (expect.startsWith(QLatin1Char('?'))) {
        *tmpl = expect.mid(1);
        *require = 0;
        *accept = 1;
    } else if (expect.startsWith(QLatin1Char('*'))) {
        *tmpl = expect.mid(1);
        *require = 0;
        *accept = INT_MAX;
    } else if (expect.startsWith(QLatin1Char('+'))) {
        *tmpl = expect.mid(1);
        *require = 1;
        *accept = INT_MAX;
    } else if (expect.startsWith(QLatin1Char('{'))) {
        int brc = expect.indexOf(QLatin1Char('}'), 1);
        if (brc < 0)
            return false;
        *tmpl = expect.mid(brc + 1);
        QString sub = expect.mid(1, brc - 1);
        int com = sub.indexOf(QLatin1Char(','));
        bool ok;
        if (com < 0) {
            *require = *accept = sub.toInt(&ok);
            return ok;
        } else {
            *require = sub.left(com).toInt();
            *accept = sub.mid(com + 1).toInt(&ok);
            if (!ok)
                *accept = INT_MAX;
            return *accept >= *require;
        }
    } else {
        *tmpl = expect;
        *require = *accept = 1;
    }
    return true;
}

void tst_lupdate::doCompare(const QStringList &actual, const QString &expectedFn, bool err)
{
    QFile file(expectedFn);
    QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(expectedFn));
    QStringList expected = QString(file.readAll()).split('\n');

    int ei = 0, ai = 0, em = expected.size(), am = actual.size();
    int oei = 0, oai = 0, oem = em, oam = am;
    int require = 0, accept = 0;
    QString tmpl;
    forever {
        if (!accept) {
            oei = ei, oai = ai;
            if (ei == em) {
                if (ai == am)
                    return;
                break;
            }
            if (!prepareMatch(expected.at(ei++), &tmpl, &require, &accept))
                QFAIL(qPrintable(QString("Malformed expected %1 at %3:%2")
                                 .arg(err ? "output" : "result").arg(ei).arg(expectedFn)));
        }
        if (ai == am) {
            if (require <= 0) {
                accept = 0;
                continue;
            }
            break;
        }
        if (err ? !QRegExp(tmpl).exactMatch(actual.at(ai)) : (actual.at(ai) != tmpl)) {
            if (require <= 0) {
                accept = 0;
                continue;
            }
            ei--;
            require = accept = 0;
            forever {
                if (!accept) {
                    oem = em, oam = am;
                    if (ei == em)
                        break;
                    if (!prepareMatch(expected.at(--em), &tmpl, &require, &accept))
                        QFAIL(qPrintable(QString("Malformed expected %1 at %3:%2")
                                         .arg(err ? "output" : "result")
                                         .arg(em + 1).arg(expectedFn)));
                }
                if (ai == am || (err ? !QRegExp(tmpl).exactMatch(actual.at(am - 1)) :
                                       (actual.at(am - 1) != tmpl))) {
                    if (require <= 0) {
                        accept = 0;
                        continue;
                    }
                    break;
                }
                accept--;
                require--;
                am--;
            }
            break;
        }
        accept--;
        require--;
        ai++;
    }
    QByteArray diff;
    for (int j = qMax(0, oai - 3); j < oai; j++)
        diff += actual.at(j) + '\n';
    diff += "<<<<<<< got\n";
    for (int j = oai; j < oam; j++) {
        diff += actual.at(j) + '\n';
        if (j >= oai + 5) {
            diff += "...\n";
            break;
        }
    }
    diff += "=========\n";
    for (int j = oei; j < oem; j++) {
        diff += expected.at(j) + '\n';
        if (j >= oei + 5) {
            diff += "...\n";
            break;
        }
    }
    diff += ">>>>>>> expected\n";
    for (int j = oam; j < qMin(oam + 3, actual.size()); j++)
        diff += actual.at(j) + '\n';
    QFAIL(qPrintable((err ? "Output for " : "Result for ") + expectedFn + " does not meet expectations:\n" + diff));
}

void tst_lupdate::doCompare(const QString &actualFn, const QString &expectedFn, bool err)
{
    QFile afile(actualFn);
    QVERIFY2(afile.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(actualFn));
    QStringList actual = QString(afile.readAll()).split('\n');

    doCompare(actual, expectedFn, err);
}

void tst_lupdate::good_data()
{
    QTest::addColumn<QString>("directory");

    QDir parsingDir(m_basePath + "good");
    QStringList dirs = parsingDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);

#ifndef Q_OS_WIN
    dirs.removeAll(QLatin1String("backslashes"));
#endif

    foreach (const QString &dir, dirs)
        QTest::newRow(dir.toLocal8Bit()) << dir;
}

void tst_lupdate::good()
{
    QFETCH(QString, directory);

    QString dir = m_basePath + "good/" + directory;

    qDebug() << "Checking...";

    QStringList generatedtsfiles(dir + QLatin1String("/project.ts"));
    QString lupdatecmd;

    QFile file(dir + "/lupdatecmd");
    if (file.exists()) {
        QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(file.fileName()));
        while (!file.atEnd()) {
            QByteArray cmdstring = file.readLine().simplified();
            if (cmdstring.startsWith('#'))
                continue;
            if (cmdstring.startsWith("lupdate")) {
                cmdstring.remove(0, 8);
                lupdatecmd.append(cmdstring);
                break;
            } else if (cmdstring.startsWith("TRANSLATION:")) {
                cmdstring.remove(0, 12);
                generatedtsfiles.clear();
                foreach (const QByteArray &s, cmdstring.split(' '))
                    if (!s.isEmpty())
                        generatedtsfiles << dir + QLatin1Char('/') + s;
            }
        }
        file.close();
    }

    foreach (const QString &ts, generatedtsfiles) {
        QFile::remove(ts);
        QString beforetsfile = ts + QLatin1String(".before");
        if (QFile::exists(beforetsfile))
            QVERIFY2(QFile::copy(beforetsfile, ts), qPrintable(beforetsfile));
    }

    if (lupdatecmd.isEmpty())
        lupdatecmd = QLatin1String("project.pro");
    lupdatecmd.prepend("-silent ");

    QProcess proc;
    proc.setWorkingDirectory(dir);
    proc.setProcessChannelMode(QProcess::MergedChannels);
    proc.start(m_cmdLupdate + ' ' + lupdatecmd, QIODevice::ReadWrite | QIODevice::Text);
    QVERIFY2(proc.waitForFinished(5000), qPrintable(lupdatecmd));
    QByteArray output = proc.readAll();
    QVERIFY2(proc.exitStatus() == QProcess::NormalExit,
             "\"lupdate " + lupdatecmd.toLatin1() + "\" crashed\n" + output);
    QVERIFY2(!proc.exitCode(),
             "\"lupdate " + lupdatecmd.toLatin1() + "\" exited with code " +
             QByteArray::number(proc.exitCode()) + "\n" + output);

    // If the file expectedoutput.txt exists, compare the
    // console output with the content of that file
    QFile outfile(dir + "/expectedoutput.txt");
    if (outfile.exists()) {
        QStringList errslist = QString::fromLatin1(output).split(QLatin1Char('\n'));
        doCompare(errslist, outfile.fileName(), true);
        if (QTest::currentTestFailed())
            return;
    }

    foreach (const QString &ts, generatedtsfiles)
        doCompare(ts, ts + QLatin1String(".result"), false);
}

void tst_lupdate::commandline_data()
{
    QTest::addColumn<QString>("currentPath");
    QTest::addColumn<QString>("commandline");
    QTest::addColumn<QString>("generatedtsfile");
    QTest::addColumn<QString>("expectedtsfile");

    QTest::newRow("Recursive scan") << QString("recursivescan")
       << QString(". -ts foo.ts") << QString("foo.ts") << QString("foo.ts.result");
    QTest::newRow("Deep path argument") << QString("recursivescan")
       << QString("sub/finddialog.cpp -ts bar.ts") << QString("bar.ts") << QString("bar.ts.result");
}

void tst_lupdate::commandline()
{
    QFETCH(QString, currentPath);
    QFETCH(QString, commandline);
    QFETCH(QString, generatedtsfile);
    QFETCH(QString, expectedtsfile);

    QString generated =
        m_basePath + currentPath + QLatin1Char('/') + generatedtsfile;
    QFile gen(generated);
    if (gen.exists())
        QVERIFY(gen.remove());
    QProcess proc;
    proc.setWorkingDirectory(m_basePath + currentPath);
    proc.setProcessChannelMode(QProcess::MergedChannels);
    proc.start(m_cmdLupdate + " -silent " + commandline, QIODevice::ReadWrite | QIODevice::Text);
    QVERIFY2(proc.waitForFinished(5000), qPrintable(commandline));
    QVERIFY2(proc.exitStatus() == QProcess::NormalExit,
             "\"lupdate -silent " + commandline.toLatin1() + "\" crashed\n" + proc.readAll());
    QVERIFY2(!proc.exitCode(),
             "\"lupdate -silent " + commandline.toLatin1() + "\" exited with code " +
             QByteArray::number(proc.exitCode()) + "\n" + proc.readAll());

    doCompare(generated, m_basePath + currentPath + QLatin1Char('/') + expectedtsfile, false);
}

#if CHECK_SIMTEXTH
void tst_lupdate::simtexth()
{
    QFETCH(QString, one);
    QFETCH(QString, two);
    QFETCH(int, expected);

    int measured = getSimilarityScore(one, two.toLatin1());
    QCOMPARE(measured, expected);
}

void tst_lupdate::simtexth_data()
{
    using namespace QTest;

    addColumn<QString>("one");
    addColumn<QString>("two");
    addColumn<int>("expected");

    newRow("00") << "" << "" << 1024;
    newRow("01") << "a" << "a" << 1024;
    newRow("02") << "ab" << "ab" << 1024;
    newRow("03") << "abc" << "abc" << 1024;
    newRow("04") << "abcd" << "abcd" << 1024;
}
#endif

QTEST_MAIN(tst_lupdate)
#include "tst_lupdate.moc"
<|endoftext|>