mirror of
https://github.com/wxWidgets/wxWidgets.git
synced 2026-03-25 04:04:29 +08:00
Restrict the input to not allow values that are greater than the positive maximum or less than the negative minimum. In many cases it is not necessary to allow invalid characters to be entered. For example, if the specified range is 0 to 100. See #24220.
383 lines
10 KiB
C++
383 lines
10 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: tests/validators/valnum.cpp
|
|
// Purpose: Unit tests for numeric validators.
|
|
// Author: Vadim Zeitlin
|
|
// Created: 2011-01-18
|
|
// Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "testprec.h"
|
|
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/app.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/textctrl.h"
|
|
#include "wx/validate.h"
|
|
#endif // WX_PRECOMP
|
|
|
|
#include "wx/valnum.h"
|
|
|
|
#include "asserthelper.h"
|
|
#include "testableframe.h"
|
|
|
|
#include "wx/scopeguard.h"
|
|
#include "wx/uiaction.h"
|
|
|
|
class NumValidatorTestCase
|
|
{
|
|
public:
|
|
NumValidatorTestCase();
|
|
~NumValidatorTestCase();
|
|
|
|
protected:
|
|
wxTextCtrl* const m_text;
|
|
|
|
wxDECLARE_NO_COPY_CLASS(NumValidatorTestCase);
|
|
};
|
|
|
|
NumValidatorTestCase::NumValidatorTestCase()
|
|
: m_text(new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY))
|
|
{
|
|
}
|
|
|
|
NumValidatorTestCase::~NumValidatorTestCase()
|
|
{
|
|
delete m_text;
|
|
}
|
|
|
|
TEST_CASE_METHOD(NumValidatorTestCase, "ValNum::TransferInt", "[valnum]")
|
|
{
|
|
int value = 0;
|
|
wxIntegerValidator<int> valInt(&value);
|
|
valInt.SetWindow(m_text);
|
|
|
|
CHECK( valInt.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "0" );
|
|
|
|
value = 17;
|
|
CHECK( valInt.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "17" );
|
|
|
|
|
|
m_text->ChangeValue("foobar");
|
|
CHECK( !valInt.TransferFromWindow() );
|
|
|
|
m_text->ChangeValue("-234");
|
|
CHECK( valInt.TransferFromWindow() );
|
|
CHECK( value == -234 );
|
|
|
|
m_text->ChangeValue("9223372036854775808"); // == LLONG_MAX + 1
|
|
CHECK( !valInt.TransferFromWindow() );
|
|
|
|
m_text->Clear();
|
|
CHECK( !valInt.TransferFromWindow() );
|
|
}
|
|
|
|
TEST_CASE_METHOD(NumValidatorTestCase, "ValNum::TransferUnsigned", "[valnum]")
|
|
{
|
|
unsigned value = 0;
|
|
wxIntegerValidator<unsigned> valUnsigned(&value);
|
|
valUnsigned.SetWindow(m_text);
|
|
|
|
CHECK( valUnsigned.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "0" );
|
|
|
|
value = 17;
|
|
CHECK( valUnsigned.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "17" );
|
|
|
|
|
|
m_text->ChangeValue("foobar");
|
|
CHECK( !valUnsigned.TransferFromWindow() );
|
|
|
|
m_text->ChangeValue("-234");
|
|
CHECK( !valUnsigned.TransferFromWindow() );
|
|
|
|
m_text->ChangeValue("234");
|
|
CHECK( valUnsigned.TransferFromWindow() );
|
|
CHECK( value == 234 );
|
|
|
|
m_text->ChangeValue("4294967295"); // == ULONG_MAX in 32 bits
|
|
CHECK( valUnsigned.TransferFromWindow() );
|
|
CHECK( value == wxUINT32_MAX );
|
|
CHECK( valUnsigned.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "4294967295" );
|
|
|
|
m_text->ChangeValue("4294967296"); // == ULONG_MAX + 1
|
|
CHECK( !valUnsigned.TransferFromWindow() );
|
|
|
|
m_text->ChangeValue("18446744073709551616"); // == ULLONG_MAX + 1
|
|
CHECK( !valUnsigned.TransferFromWindow() );
|
|
|
|
m_text->Clear();
|
|
CHECK( !valUnsigned.TransferFromWindow() );
|
|
}
|
|
|
|
TEST_CASE_METHOD(NumValidatorTestCase, "ValNum::TransferUnsignedRange", "[valnum]")
|
|
{
|
|
unsigned value = 1;
|
|
wxIntegerValidator<unsigned> valUnsigned(&value, 1, 20);
|
|
valUnsigned.SetWindow(m_text);
|
|
|
|
CHECK( valUnsigned.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "1" );
|
|
|
|
value = 17;
|
|
CHECK( valUnsigned.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "17" );
|
|
|
|
m_text->ChangeValue("foobar");
|
|
CHECK( !valUnsigned.TransferFromWindow() );
|
|
|
|
m_text->ChangeValue("0");
|
|
CHECK( !valUnsigned.TransferFromWindow() );
|
|
|
|
m_text->ChangeValue("1");
|
|
CHECK( valUnsigned.TransferFromWindow() );
|
|
CHECK( value == 1);
|
|
|
|
m_text->ChangeValue("20");
|
|
CHECK( valUnsigned.TransferFromWindow() );
|
|
CHECK( value == 20);
|
|
|
|
m_text->ChangeValue("21");
|
|
CHECK( !valUnsigned.TransferFromWindow() );
|
|
|
|
m_text->Clear();
|
|
CHECK( !valUnsigned.TransferFromWindow() );
|
|
}
|
|
|
|
TEST_CASE_METHOD(NumValidatorTestCase, "ValNum::TransferULL", "[valnum]")
|
|
{
|
|
unsigned long long value = 0;
|
|
wxIntegerValidator<unsigned long long> valULL(&value);
|
|
valULL.SetWindow(m_text);
|
|
|
|
SECTION("LLONG_MAX")
|
|
{
|
|
m_text->ChangeValue("9223372036854775807"); // == LLONG_MAX
|
|
REQUIRE( valULL.TransferFromWindow() );
|
|
CHECK( value == static_cast<wxULongLong_t>(wxINT64_MAX) );
|
|
|
|
REQUIRE( valULL.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "9223372036854775807" );
|
|
}
|
|
|
|
SECTION("LLONG_MAX+1")
|
|
{
|
|
m_text->ChangeValue("9223372036854775808"); // == LLONG_MAX + 1
|
|
REQUIRE( valULL.TransferFromWindow() );
|
|
CHECK( value == static_cast<wxULongLong_t>(wxINT64_MAX) + 1 );
|
|
|
|
REQUIRE( valULL.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "9223372036854775808" );
|
|
}
|
|
|
|
SECTION("ULLONG_MAX")
|
|
{
|
|
m_text->ChangeValue("18446744073709551615"); // == ULLONG_MAX
|
|
REQUIRE( valULL.TransferFromWindow() );
|
|
CHECK( value == wxUINT64_MAX );
|
|
|
|
REQUIRE( valULL.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "18446744073709551615" );
|
|
}
|
|
|
|
SECTION("ULLONG_MAX+1")
|
|
{
|
|
m_text->ChangeValue("18446744073709551616"); // == ULLONG_MAX + 1
|
|
CHECK( !valULL.TransferFromWindow() );
|
|
}
|
|
}
|
|
|
|
TEST_CASE_METHOD(NumValidatorTestCase, "ValNum::TransferFloat", "[valnum]")
|
|
{
|
|
// We need a locale with point as decimal separator.
|
|
wxLocale loc(wxLANGUAGE_ENGLISH_UK, wxLOCALE_DONT_LOAD_DEFAULT);
|
|
|
|
float value = 0;
|
|
wxFloatingPointValidator<float> valFloat(3, &value);
|
|
valFloat.SetWindow(m_text);
|
|
|
|
CHECK( valFloat.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "0.000" );
|
|
|
|
value = 1.234f;
|
|
CHECK( valFloat.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "1.234" );
|
|
|
|
value = 1.2345678f;
|
|
CHECK( valFloat.TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "1.235" );
|
|
|
|
|
|
m_text->ChangeValue("foobar");
|
|
CHECK( !valFloat.TransferFromWindow() );
|
|
|
|
m_text->ChangeValue("-234.567");
|
|
CHECK( valFloat.TransferFromWindow() );
|
|
CHECK( value == -234.567f );
|
|
|
|
m_text->Clear();
|
|
CHECK( !valFloat.TransferFromWindow() );
|
|
}
|
|
|
|
TEST_CASE_METHOD(NumValidatorTestCase, "ValNum::ZeroAsBlank", "[valnum]")
|
|
{
|
|
long value = 0;
|
|
m_text->SetValidator(
|
|
wxMakeIntegerValidator(&value, wxNUM_VAL_ZERO_AS_BLANK));
|
|
|
|
wxValidator * const val = m_text->GetValidator();
|
|
|
|
CHECK( val->TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "" );
|
|
|
|
value++;
|
|
CHECK( val->TransferFromWindow() );
|
|
CHECK( value == 0 );
|
|
}
|
|
|
|
TEST_CASE_METHOD(NumValidatorTestCase, "ValNum::NoTrailingZeroes", "[valnum]")
|
|
{
|
|
// We need a locale with point as decimal separator.
|
|
wxLocale loc(wxLANGUAGE_ENGLISH_UK, wxLOCALE_DONT_LOAD_DEFAULT);
|
|
|
|
double value = 1.2;
|
|
m_text->SetValidator(
|
|
wxMakeFloatingPointValidator(3, &value, wxNUM_VAL_NO_TRAILING_ZEROES));
|
|
|
|
wxValidator * const val = m_text->GetValidator();
|
|
|
|
CHECK( val->TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "1.2" );
|
|
|
|
value = 1.234;
|
|
CHECK( val->TransferToWindow() );
|
|
CHECK( m_text->GetValue() == "1.234" );
|
|
}
|
|
|
|
#if wxUSE_UIACTIONSIMULATOR
|
|
|
|
TEST_CASE_METHOD(NumValidatorTestCase, "ValNum::Interactive", "[valnum]")
|
|
{
|
|
// Set a locale using comma as thousands separator character.
|
|
wxLocale loc(wxLANGUAGE_ENGLISH_UK, wxLOCALE_DONT_LOAD_DEFAULT);
|
|
|
|
m_text->SetValidator(
|
|
wxIntegerValidator<unsigned>(nullptr, wxNUM_VAL_THOUSANDS_SEPARATOR));
|
|
|
|
// Create a sibling text control to be able to switch focus and thus
|
|
// trigger the control validation/normalization.
|
|
wxTextCtrl * const text2 = new wxTextCtrl(m_text->GetParent(), wxID_ANY);
|
|
wxON_BLOCK_EXIT_OBJ0( *text2, wxWindow::Destroy );
|
|
text2->Move(10, 80); // Just to see it better while debugging...
|
|
wxFloatingPointValidator<float> valFloat(3);
|
|
valFloat.SetRange(-10., 10.);
|
|
text2->SetValidator(valFloat);
|
|
|
|
wxUIActionSimulator sim;
|
|
|
|
// Entering '-' in a control with positive range is not allowed.
|
|
m_text->SetFocus();
|
|
wxYield();
|
|
sim.Char('-');
|
|
wxYield();
|
|
CHECK( m_text->GetValue() == "" );
|
|
|
|
// Neither is entering '.' or any non-digit character.
|
|
sim.Text(".a+/");
|
|
wxYield();
|
|
CHECK( m_text->GetValue() == "" );
|
|
|
|
// Entering digits should work though and after leaving the control the
|
|
// contents should be normalized.
|
|
sim.Text("1234567");
|
|
wxYield();
|
|
text2->SetFocus();
|
|
wxYield();
|
|
if ( loc.IsOk() )
|
|
CHECK( m_text->GetValue() == "1,234,567" );
|
|
else
|
|
CHECK( m_text->GetValue() == "1234567" );
|
|
|
|
|
|
// Entering both '-' and '.' in this control should work but only in the
|
|
// correct order.
|
|
sim.Char('-');
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "-" );
|
|
|
|
text2->SetInsertionPoint(0);
|
|
sim.Char('.');
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "-" );
|
|
|
|
text2->SetInsertionPointEnd();
|
|
sim.Char('.');
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "-." );
|
|
|
|
// Adding up to three digits after the point should work.
|
|
sim.Text("987");
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "-.987" );
|
|
|
|
// But no more.
|
|
sim.Text("654");
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "-.987" );
|
|
|
|
// We can remove one digit and another one though.
|
|
sim.Char(WXK_BACK);
|
|
sim.Char(WXK_BACK);
|
|
sim.Char('6');
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "-.96" );
|
|
|
|
|
|
// Also test the range constraint.
|
|
valFloat.SetRange(10., 20.);
|
|
text2->SetValidator(valFloat);
|
|
text2->Clear();
|
|
|
|
// Entering a value which is out of range but within
|
|
// the extended input range is allowed.
|
|
sim.Char('9');
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "9" );
|
|
|
|
// Entering a value greater than the positive range maximum
|
|
// is not allowed.
|
|
sim.Char('9');
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "9" );
|
|
|
|
// A value that is out of range but within the extended input
|
|
// range must be clamped to the valid range on focus loss.
|
|
m_text->SetFocus();
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "10.000" );
|
|
|
|
// Repeat the test with a negative invalid value.
|
|
valFloat.SetRange(-20., -10.);
|
|
text2->SetValidator(valFloat);
|
|
text2->Clear();
|
|
text2->SetFocus();
|
|
|
|
sim.Text("-2");
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "-2" );
|
|
|
|
sim.Char('2');
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "-2" );
|
|
|
|
m_text->SetFocus();
|
|
wxYield();
|
|
CHECK( text2->GetValue() == "-10.000" );
|
|
}
|
|
|
|
#endif // wxUSE_UIACTIONSIMULATOR
|