Add new custom types and unit tests for configuration

patch by Ekaterina Dimitrova; reviewed by Caleb Rackliffe, David Capwell, Michael Semb Wever and Benjamin Lerer for CASSANDRA-15234
This commit is contained in:
Ekaterina Dimitrova 2021-12-14 23:00:56 -05:00
parent da47849b50
commit db9f7a67ec
8 changed files with 1561 additions and 0 deletions

View File

@ -1,4 +1,5 @@
4.1
* Standardize storage configuration parameters' names. Support unit suffixes. (CASSANDRA-15234)
* Remove support for Windows (CASSANDRA-16956)
* Runtime-configurable YAML option to prohibit USE statements (CASSANDRA-17318)
* When streaming sees a ClosedChannelException this triggers the disk failure policy (CASSANDRA-17116)

View File

@ -81,6 +81,12 @@ New features
Upgrading
---------
- There is a new cassandra.yaml version 2. Units suffixes should be provided for all rates(B/s|MiB/s|KiB/s|MiB/s),
memory (KiB|MiB|GiB|B) and duration(d|h|s|ms|us|µs|ns|m)
parameters. (CASSANDRA-15234)
Backward compatibility with the old cassandra.yaml file will be in place until at least the next major version.
- Many cassandra.yaml parameters' names have been changed. Full list can be found on ...... (ADD LINK LATER WHEN PAGE
IS CREATED) (CASSANDRA-15234)
- Before you upgrade, if you are using `cassandra.auth_bcrypt_gensalt_log2_rounds` property,
confirm it is set to value lower than 31 otherwise Cassandra will fail to start. See CASSANDRA-9384
for further details. You also need to regenerate passwords for users for who the password

View File

@ -0,0 +1,378 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.config;
import java.util.Arrays;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.google.common.primitives.Ints;
import org.apache.cassandra.exceptions.ConfigurationException;
/**
* Represents a data rate type used for cassandra configuration. It supports the opportunity for the users to be able to
* add units to the confiuration parameter value. (CASSANDRA-15234)
*/
public final class DataRateSpec
{
/**
* The Regexp used to parse the rate provided as String in cassandra.yaml.
*/
private static final Pattern BIT_RATE_UNITS_PATTERN = Pattern.compile("^(\\d+)(MiB/s|KiB/s|B/s)$");
private final double quantity;
private final DataRateUnit unit;
public DataRateSpec(String value)
{
//parse the string field value
Matcher matcher = BIT_RATE_UNITS_PATTERN.matcher(value);
if (!matcher.find())
throw new ConfigurationException("Invalid bit rate: " + value + " Accepted units: MiB/s, KiB/s, B/s where " +
"case matters and " + "only non-negative values are valid");
quantity = Long.parseLong(matcher.group(1));
unit = DataRateUnit.fromSymbol(matcher.group(2));
}
DataRateSpec(double quantity, DataRateUnit unit)
{
if (quantity < 0)
throw new ConfigurationException("Invalid bit rare: value must be non-negative");
if (quantity > Long.MAX_VALUE)
throw new NumberFormatException("Invalid bit rate: value must be between 0 and Long.MAX_VALUE = 9223372036854775807");
this.quantity = quantity;
this.unit = unit;
}
/**
* Creates a {@code DataRateSpec} of the specified amount of bits per second.
*
* @param bytesPerSecond the amount of bytes per second
* @return a {@code DataRateSpec}
*/
public static DataRateSpec inBytesPerSecond(long bytesPerSecond)
{
return new DataRateSpec(bytesPerSecond, DataRateUnit.BYTES_PER_SECOND);
}
/**
* Creates a {@code DataRateSpec} of the specified amount of kibibytes per second.
*
* @param kibibytesPerSecond the amount of kibibytes per second
* @return a {@code DataRateSpec}
*/
public static DataRateSpec inKibibytesPerSecond(long kibibytesPerSecond)
{
return new DataRateSpec(kibibytesPerSecond, DataRateUnit.KIBIBYTES_PER_SECOND);
}
/**
* Creates a {@code DataRateSpec} of the specified amount of mebibytes per second.
*
* @param mebibytesPerSecond the amount of mebibytes per second
* @return a {@code DataRateSpec}
*/
public static DataRateSpec inMebibytesPerSecond(long mebibytesPerSecond)
{
return new DataRateSpec(mebibytesPerSecond, DataRateUnit.MEBIBYTES_PER_SECOND);
}
/**
* Creates a {@code DataRateSpec} of the specified amount of mebibytes per second.
*
* @param megabitsPerSecond the amount of megabits per second
* @return a {@code DataRateSpec}
*/
public static DataRateSpec megabitsPerSecondInMebibytesPerSecond(long megabitsPerSecond)
{
final double MEBIBYTES_PER_MEGABIT = 0.119209289550781;
double mebibytesPerSecond = (double)megabitsPerSecond * MEBIBYTES_PER_MEGABIT;
return new DataRateSpec(mebibytesPerSecond, DataRateUnit.MEBIBYTES_PER_SECOND);
}
/**
* @return the data rate unit assigned.
*/
public DataRateUnit getUnit()
{
return unit;
}
/**
* @return the data rate in bytes per second
*/
public double toBytesPerSecond()
{
return unit.toBytesPerSecond(quantity);
}
/**
* Returns the data rate in bytes per second as an {@code int}
*
* @return the data rate in bytes per second or {@code Integer.MAX_VALUE} if the rate is too large.
*/
public int toBytesPerSecondAsInt()
{
return Ints.saturatedCast(Math.round(toBytesPerSecond()));
}
/**
* @return the data rate in kibibytes per second
*/
public double toKibibytesPerSecond()
{
return unit.toKibibytesPerSecond(quantity);
}
/**
* Returns the data rate in kibibytes per second as an {@code int}
*
* @return the data rate in kibibytes per second or {@code Integer.MAX_VALUE} if the number of kibibytes is too large.
*/
public int toKibibytesPerSecondAsInt()
{
return Ints.saturatedCast(Math.round(toKibibytesPerSecond()));
}
/**
* @return the data rate in mebibytes per second
*/
public double toMebibytesPerSecond()
{
return unit.toMebibytesPerSecond(quantity);
}
/**
* Returns the data rate in mebibytes per second as an {@code int}
*
* @return the data rate in mebibytes per second or {@code Integer.MAX_VALUE} if the number of mebibytes is too large.
*/
public int toMebibytesPerSecondAsInt()
{
return Ints.saturatedCast(Math.round(toMebibytesPerSecond()));
}
/**
* This method is required in order to support backward compatibility with the old unit used for a few Data Rate
* parameters before CASSANDRA-15234
*
* @return the data rate in megabits per second.
*/
public double toMegabitsPerSecond()
{
return unit.toMegabitsPerSecond(quantity);
}
/**
* Returns the data rate in megabits per second as an {@code int}. This method is required in order to support
* backward compatibility with the old unit used for a few Data Rate parameters before CASSANDRA-15234
*
* @return the data rate in mebibytes per second or {@code Integer.MAX_VALUE} if the number of mebibytes is too large.
*/
public int toMegabitsPerSecondAsInt()
{
return Ints.saturatedCast(Math.round(toMegabitsPerSecond()));
}
@Override
public int hashCode()
{
return Objects.hash(unit.toKibibytesPerSecond(quantity));
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (!(obj instanceof DataRateSpec))
return false;
DataRateSpec other = (DataRateSpec) obj;
if (unit == other.unit)
return quantity == other.quantity;
// Due to overflows we can only guarantee that the 2 data rates are equal if we get the same results
// doing the conversion in both directions.
return unit.convert(other.quantity, other.unit) == quantity && other.unit.convert(quantity, unit) == other.quantity;
}
@Override
public String toString()
{
return Math.round(quantity) + unit.symbol;
}
public enum DataRateUnit
{
BYTES_PER_SECOND("B/s")
{
public double toBytesPerSecond(double d)
{
return d;
}
public double toKibibytesPerSecond(double d)
{
return d / 1024.0;
}
public double toMebibytesPerSecond(double d)
{
return d / (1024.0 * 1024.0);
}
public double toMegabitsPerSecond(double d) { return (d / 125000.0); }
public double convert(double source, DataRateUnit sourceUnit)
{
return sourceUnit.toBytesPerSecond(source);
}
},
KIBIBYTES_PER_SECOND("KiB/s")
{
public double toBytesPerSecond(double d)
{
return x(d, 1024.0, (MAX / 1024.0));
}
public double toKibibytesPerSecond(double d)
{
return d;
}
public double toMebibytesPerSecond(double d)
{
return d / 1024.0;
}
public double toMegabitsPerSecond(double d)
{
return d / 122.0;
}
public double convert(double source, DataRateUnit sourceUnit)
{
return sourceUnit.toKibibytesPerSecond(source);
}
},
MEBIBYTES_PER_SECOND("MiB/s")
{
public double toBytesPerSecond(double d)
{
return x(d, (1024.0 * 1024.0), (MAX / (1024.0 * 1024.0)));
}
public double toKibibytesPerSecond(double d)
{
return x(d, 1024.0, (MAX / 1024.0));
}
public double toMebibytesPerSecond(double d)
{
return d;
}
public double toMegabitsPerSecond(double d)
{
if (d > MAX / (MEGABITS_PER_MEBIBYTE))
return MAX;
return Math.round(d * MEGABITS_PER_MEBIBYTE);
}
public double convert(double source, DataRateUnit sourceUnit)
{
return sourceUnit.toMebibytesPerSecond(source);
}
};
static final double MAX = Long.MAX_VALUE;
static final double MEGABITS_PER_MEBIBYTE = 8.388608;
/**
* Scale d by m, checking for overflow. This has a short name to make above code more readable.
*/
static double x(double d, double m, double over)
{
assert (over > 0.0) && (over < (MAX - 1)) && (over == (MAX / m));
if (d > over)
return MAX;
return d * m;
}
/**
* @param symbol the unit symbol
* @return the rate unit corresponding to the given symbol
*/
public static DataRateUnit fromSymbol(String symbol)
{
for (DataRateUnit value : values())
{
if (value.symbol.equalsIgnoreCase(symbol))
return value;
}
throw new ConfigurationException(String.format("Unsupported data rate unit: %s. Supported units are: %s",
symbol, Arrays.stream(values())
.map(u -> u.symbol)
.collect(Collectors.joining(", "))));
}
/**
* The unit symbol
*/
private final String symbol;
DataRateUnit(String symbol)
{
this.symbol = symbol;
}
public double toBytesPerSecond(double d)
{
throw new AbstractMethodError();
}
public double toKibibytesPerSecond(double d)
{
throw new AbstractMethodError();
}
public double toMebibytesPerSecond(double d)
{
throw new AbstractMethodError();
}
public double toMegabitsPerSecond(double d) { throw new AbstractMethodError(); }
public double convert(double source, DataRateUnit sourceUnit)
{
throw new AbstractMethodError();
}
}
}

View File

@ -0,0 +1,397 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.config;
import java.util.Arrays;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.google.common.primitives.Ints;
import org.apache.cassandra.exceptions.ConfigurationException;
/**
* Represents an amount of data storage. Wrapper class for Cassandra configuration parameters, providing to the
* users the opportunity to be able to provide config with a unit of their choice in cassandra.yaml as per the available
* options. (CASSANDRA-15234)
*/
public final class DataStorageSpec
{
/**
* The Regexp used to parse the storage provided as String.
*/
private static final Pattern STORAGE_UNITS_PATTERN = Pattern.compile("^(\\d+)(GiB|MiB|KiB|B)$");
private final long quantity;
private final DataStorageUnit unit;
public DataStorageSpec(String value)
{
if (value == null || value.equals("null"))
{
quantity = 0;
unit = DataStorageUnit.MEBIBYTES; // the unit doesn't really matter as 0 is 0 in all units
return;
}
//parse the string field value
Matcher matcher = STORAGE_UNITS_PATTERN.matcher(value);
if (!matcher.find())
{
throw new ConfigurationException("Invalid data storage: " + value + " Accepted units: B, KiB, MiB, GiB" +
" where case matters and only non-negative values are accepted");
}
quantity = Long.parseLong(matcher.group(1));
unit = DataStorageUnit.fromSymbol(matcher.group(2));
}
DataStorageSpec(long quantity, DataStorageUnit unit)
{
if (quantity < 0)
throw new ConfigurationException("Invalid data storage: value must be positive, but was " + quantity);
this.quantity = quantity;
this.unit = unit;
}
/**
* Creates a {@code DataStorageSpec} of the specified amount of bytes.
*
* @param bytes the amount of bytes
* @return a {@code DataStorageSpec}
*/
public static DataStorageSpec inBytes(long bytes)
{
return new DataStorageSpec(bytes, DataStorageUnit.BYTES);
}
/**
* Creates a {@code DataStorageSpec} of the specified amount of kibibytes.
*
* @param kibibytes the amount of kibibytes
* @return a {@code DataStorageSpec}
*/
public static DataStorageSpec inKibibytes(long kibibytes)
{
return new DataStorageSpec(kibibytes, DataStorageUnit.KIBIBYTES);
}
/**
* Creates a {@code DataStorageSpec} of the specified amount of mebibytes.
*
* @param mebibytes the amount of mebibytes
* @return a {@code DataStorageSpec}
*/
public static DataStorageSpec inMebibytes(long mebibytes)
{
return new DataStorageSpec(mebibytes, DataStorageUnit.MEBIBYTES);
}
/**
* @return the data storage unit.
*/
public DataStorageUnit getUnit()
{
return unit;
}
/**
* @return the amount of data storage in bytes
*/
public long toBytes()
{
return unit.toBytes(quantity);
}
/**
* Returns the amount of data storage in bytes as an {@code int}
*
* @return the amount of data storage in bytes or {@code Integer.MAX_VALUE} if the number of bytes is too large.
*/
public int toBytesAsInt()
{
return Ints.saturatedCast(toBytes());
}
/**
* @return the amount of data storage in kibibytes
*/
public long toKibibytes()
{
return unit.toKibibytes(quantity);
}
/**
* Returns the amount of data storage in kibibytes as an {@code int}
*
* @return the amount of data storage in kibibytes or {@code Integer.MAX_VALUE} if the number of kibibytes is too large.
*/
public int toKibibytesAsInt()
{
return Ints.saturatedCast(toKibibytes());
}
/**
* @return the amount of data storage in mebibytes
*/
public long toMebibytes()
{
return unit.toMebibytes(quantity);
}
/**
* Returns the amount of data storage in mebibytes as an {@code int}
*
* @return the amount of data storage in mebibytes or {@code Integer.MAX_VALUE} if the number of mebibytes is too large.
*/
public int toMebibytesAsInt()
{
return Ints.saturatedCast(toMebibytes());
}
/**
* @return the amount of data storage in gibibytes
*/
public long toGibibytes()
{
return unit.toGibibytes(quantity);
}
/**
* Returns the amount of data storage in gibibytes as an {@code int}
*
* @return the amount of data storage in gibibytes or {@code Integer.MAX_VALUE} if the number of gibibytes is too large.
*/
public int toGibibytesAsInt()
{
return Ints.saturatedCast(toGibibytes());
}
@Override
public int hashCode()
{
return Objects.hash(unit.toKibibytes(quantity));
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (!(obj instanceof DataStorageSpec))
return false;
DataStorageSpec other = (DataStorageSpec) obj;
if (unit == other.unit)
return quantity == other.quantity;
// Due to overflows we can only guarantee that the 2 storages are equal if we get the same results
// doing the convertion in both directions.
return unit.convert(other.quantity, other.unit) == quantity && other.unit.convert(quantity, unit) == other.quantity;
}
@Override
public String toString()
{
return quantity + unit.symbol;
}
public enum DataStorageUnit
{
BYTES("B")
{
public long toBytes(long d)
{
return d;
}
public long toKibibytes(long d)
{
return (d / 1024L);
}
public long toMebibytes(long d)
{
return (d / (1024L * 1024));
}
public long toGibibytes(long d)
{
return (d / (1024L * 1024 * 1024));
}
public long convert(long source, DataStorageUnit sourceUnit)
{
return sourceUnit.toBytes(source);
}
},
KIBIBYTES("KiB")
{
public long toBytes(long d)
{
return x(d, 1024L, (MAX / 1024L));
}
public long toKibibytes(long d)
{
return d;
}
public long toMebibytes(long d)
{
return (d / 1024L);
}
public long toGibibytes(long d)
{
return (d / (1024L * 1024));
}
public long convert(long source, DataStorageUnit sourceUnit)
{
return sourceUnit.toKibibytes(source);
}
},
MEBIBYTES("MiB")
{
public long toBytes(long d)
{
return x(d, (1024L * 1024), MAX / (1024L * 1024));
}
public long toKibibytes(long d)
{
return x(d, 1024L, (MAX / 1024L));
}
public long toMebibytes(long d)
{
return d;
}
public long toGibibytes(long d)
{
return (d / 1024L);
}
public long convert(long source, DataStorageUnit sourceUnit)
{
return sourceUnit.toMebibytes(source);
}
},
GIBIBYTES("GiB")
{
public long toBytes(long d)
{
return x(d, (1024L * 1024 * 1024), MAX / (1024L * 1024 * 1024));
}
public long toKibibytes(long d)
{
return x(d, (1024L * 1024), MAX / (1024L * 1024));
}
public long toMebibytes(long d)
{
return x(d, 1024L, (MAX / 1024L));
}
public long toGibibytes(long d)
{
return d;
}
public long convert(long source, DataStorageUnit sourceUnit)
{
return sourceUnit.toGibibytes(source);
}
};
/**
* Scale d by m, checking for overflow. This has a short name to make above code more readable.
*/
static long x(long d, long m, long over)
{
assert (over > 0) && (over < (MAX-1L)) && (over == (MAX / m));
if (d > over)
return Long.MAX_VALUE;
return Math.multiplyExact(d, m);
}
/**
* @param symbol the unit symbol
* @return the memory unit corresponding to the given symbol
*/
public static DataStorageUnit fromSymbol(String symbol)
{
for (DataStorageUnit value : values())
{
if (value.symbol.equalsIgnoreCase(symbol))
return value;
}
throw new ConfigurationException(String.format("Unsupported data storage unit: %s. Supported units are: %s",
symbol, Arrays.stream(values())
.map(u -> u.symbol)
.collect(Collectors.joining(", "))));
}
static final long MAX = Long.MAX_VALUE;
/**
* The unit symbol
*/
private final String symbol;
DataStorageUnit(String symbol)
{
this.symbol = symbol;
}
public long toBytes(long d)
{
throw new AbstractMethodError();
}
public long toKibibytes(long d)
{
throw new AbstractMethodError();
}
public long toMebibytes(long d)
{
throw new AbstractMethodError();
}
public long toGibibytes(long d)
{
throw new AbstractMethodError();
}
public long convert(long source, DataStorageUnit sourceUnit)
{
throw new AbstractMethodError();
}
}
}

View File

@ -0,0 +1,342 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.config;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.google.common.primitives.Ints;
import org.apache.cassandra.exceptions.ConfigurationException;
/**
* Represents a positive time duration. Wrapper class for Cassandra duration configuration parameters, providing to the
* users the opportunity to be able to provide config with a unit of their choice in cassandra.yaml as per the available
* options. (CASSANDRA-15234)
*/
public final class DurationSpec
{
/**
* The Regexp used to parse the duration provided as String.
*/
private static final Pattern TIME_UNITS_PATTERN = Pattern.compile(("^(\\d+)(d|h|s|ms|us|µs|ns|m)"));
private static final Pattern VALUES_PATTERN = Pattern.compile(("\\d+"));
public final long quantity;
private final TimeUnit unit;
public DurationSpec(String value)
{
if (value == null || value.equals("null") || value.toLowerCase(Locale.ROOT).equals("nan"))
{
quantity = 0;
unit = TimeUnit.MILLISECONDS;
return;
}
//parse the string field value
Matcher matcher = TIME_UNITS_PATTERN.matcher(value);
if(matcher.find())
{
quantity = Long.parseLong(matcher.group(1));
unit = fromSymbol(matcher.group(2));
}
else
{
throw new ConfigurationException("Invalid duration: " + value + " Accepted units: d, h, m, s, ms, us, µs," +
" ns where case matters and " + "only non-negative values");
}
}
DurationSpec(long quantity, TimeUnit unit)
{
if (quantity < 0)
throw new ConfigurationException("Invalid duration: value must be positive");
this.quantity = quantity;
this.unit = unit;
}
private DurationSpec(double quantity, TimeUnit unit)
{
this(Math.round(quantity), unit);
}
/**
* Creates a {@code DurationSpec} of the specified amount of milliseconds.
*
* @param milliseconds the amount of milliseconds
* @return a duration
*/
public static DurationSpec inMilliseconds(long milliseconds)
{
return new DurationSpec(milliseconds, TimeUnit.MILLISECONDS);
}
public static DurationSpec inDoubleMilliseconds(double milliseconds)
{
return new DurationSpec(milliseconds, TimeUnit.MILLISECONDS);
}
/**
* Creates a {@code DurationSpec} of the specified amount of seconds.
*
* @param seconds the amount of seconds
* @return a duration
*/
public static DurationSpec inSeconds(long seconds)
{
return new DurationSpec(seconds, TimeUnit.SECONDS);
}
/**
* Creates a {@code DurationSpec} of the specified amount of minutes.
*
* @param minutes the amount of minutes
* @return a duration
*/
public static DurationSpec inMinutes(long minutes)
{
return new DurationSpec(minutes, TimeUnit.MINUTES);
}
/**
* Creates a {@code DurationSpec} of the specified amount of hours.
*
* @param hours the amount of hours
* @return a duration
*/
public static DurationSpec inHours(long hours)
{
return new DurationSpec(hours, TimeUnit.HOURS);
}
/**
* Creates a {@code DurationSpec} of the specified amount of seconds. Custom method for special cases.
*
* @param value which can be in the old form only presenting the quantity or the post CASSANDRA-15234 form - a
* value consisting of quantity and unit. This method is necessary for three parameters which didn't change their
* names but only their value format. (key_cache_save_period, row_cache_save_period, counter_cache_save_period)
* @return a duration
*/
public static DurationSpec inSecondsString(String value)
{
//parse the string field value
Matcher matcher = VALUES_PATTERN.matcher(value);
long seconds;
//if the provided string value is just a number, then we create a Duration Spec value in seconds
if (matcher.matches())
{
seconds = Long.parseLong(value);
return new DurationSpec(seconds, TimeUnit.SECONDS);
}
//otherwise we just use the standard constructors
return new DurationSpec(value);
}
/**
* @param symbol the time unit symbol
* @return the time unit associated to the specified symbol
*/
static TimeUnit fromSymbol(String symbol)
{
switch (symbol.toLowerCase())
{
case "d": return TimeUnit.DAYS;
case "h": return TimeUnit.HOURS;
case "m": return TimeUnit.MINUTES;
case "s": return TimeUnit.SECONDS;
case "ms": return TimeUnit.MILLISECONDS;
case "us":
case "µs": return TimeUnit.MICROSECONDS;
case "ns": return TimeUnit.NANOSECONDS;
}
throw new ConfigurationException(String.format("Unsupported time unit: %s. Supported units are: %s",
symbol, Arrays.stream(TimeUnit.values())
.map(DurationSpec::getSymbol)
.collect(Collectors.joining(", "))));
}
/**
* @param targetUnit the time unit
* @return this duration in the specified time unit
*/
public long to(TimeUnit targetUnit)
{
return targetUnit.convert(quantity, unit);
}
/**
* @return this duration in number of hours
*/
public long toHours()
{
return unit.toHours(quantity);
}
/**
* Returns this duration in number of minutes as an {@code int}
*
* @return this duration in number of minutes or {@code Integer.MAX_VALUE} if the number of minutes is too large.
*/
public int toHoursAsInt()
{
return Ints.saturatedCast(toHours());
}
/**
* @return this duration in number of minutes
*/
public long toMinutes()
{
return unit.toMinutes(quantity);
}
/**
* Returns this duration in number of minutes as an {@code int}
*
* @return this duration in number of minutes or {@code Integer.MAX_VALUE} if the number of minutes is too large.
*/
public int toMinutesAsInt()
{
return Ints.saturatedCast(toMinutes());
}
/**
* @return this duration in number of seconds
*/
public long toSeconds()
{
return unit.toSeconds(quantity);
}
/**
* Returns this duration in number of seconds as an {@code int}
*
* @return this duration in number of seconds or {@code Integer.MAX_VALUE} if the number of seconds is too large.
*/
public int toSecondsAsInt()
{
return Ints.saturatedCast(toSeconds());
}
/**
* @return this duration in number of nanoseconds
*/
public long toNanoseconds()
{
return unit.toNanos(quantity);
}
/**
* Returns this duration in number of nanoseconds as an {@code int}
*
* @return this duration in number of nanoseconds or {@code Integer.MAX_VALUE} if the number of nanoseconds is too large.
*/
public int toNanosecondsAsInt()
{
return Ints.saturatedCast(toNanoseconds());
}
/**
* @return this duration in number of milliseconds
*/
public long toMilliseconds()
{
return unit.toMillis(quantity);
}
/**
* @return the duration value in milliseconds
*/
public static long toMilliseconds(DurationSpec quantity)
{
return quantity.toMilliseconds();
}
/**
* Returns this duration in number of milliseconds as an {@code int}
*
* @return this duration in number of milliseconds or {@code Integer.MAX_VALUE} if the number of milliseconds is too large.
*/
public int toMillisecondsAsInt()
{
return Ints.saturatedCast(toMilliseconds());
}
@Override
public int hashCode()
{
// Milliseconds seems to be a reasonable tradeoff
return Objects.hash(unit.toMillis(quantity));
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (!(obj instanceof DurationSpec))
return false;
DurationSpec other = (DurationSpec) obj;
if (unit == other.unit)
return quantity == other.quantity;
// Due to overflows we can only guarantee that the 2 durations are equal if we get the same results
// doing the conversion in both directions.
return unit.convert(other.quantity, other.unit) == quantity && other.unit.convert(quantity, unit) == other.quantity;
}
@Override
public String toString()
{
return quantity + getSymbol(unit);
}
/**
* Returns the symbol associated to the specified unit
*
* @param unit the time unit
* @return the time unit symbol
*/
static String getSymbol(TimeUnit unit)
{
switch (unit)
{
case DAYS: return "d";
case HOURS: return "h";
case MINUTES: return "m";
case SECONDS: return "s";
case MILLISECONDS: return "ms";
case MICROSECONDS: return "us";
case NANOSECONDS: return "ns";
}
throw new AssertionError();
}
}

View File

@ -0,0 +1,136 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.config;
import org.junit.Test;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.quicktheories.core.Gen;
import org.quicktheories.generators.SourceDSL;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.*;
import static org.quicktheories.QuickTheory.qt;
public class DataRateSpecTest
{
@Test
public void testConversions()
{
assertEquals(10, new DataRateSpec("10B/s").toBytesPerSecond(), 0);
assertEquals(10240, new DataRateSpec("10KiB/s").toBytesPerSecond(), 0);
assertEquals(0, new DataRateSpec("10KiB/s").toMebibytesPerSecond(), 0.1);
assertEquals(10240, new DataRateSpec("10MiB/s").toKibibytesPerSecond(), 0);
assertEquals(10485760, new DataRateSpec("10MiB/s").toBytesPerSecond(), 0);
assertEquals(10485760, new DataRateSpec("10MiB/s").toBytesPerSecond(), 0);
assertEquals(new DataRateSpec("24MiB/s").toString(), DataRateSpec.megabitsPerSecondInMebibytesPerSecond(200L).toString());
}
@Test
public void testOverflowingDuringConversion()
{
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807B/s").toBytesPerSecond(), 0);
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807B/s").toBytesPerSecondAsInt(), 0);
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807KiB/s").toBytesPerSecond(), 0);
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807KiB/s").toBytesPerSecondAsInt(), 0);
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toBytesPerSecond(), 0);
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toBytesPerSecondAsInt(), 0);
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toBytesPerSecond(), 0);
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toBytesPerSecondAsInt(), 0);
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toMegabitsPerSecond(), 0);
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toMegabitsPerSecondAsInt());
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807KiB/s").toKibibytesPerSecond(), 0);
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807KiB/s").toKibibytesPerSecondAsInt());
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toKibibytesPerSecond(), 0);
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toKibibytesPerSecondAsInt());
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toMebibytesPerSecond(), 0);
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toMebibytesPerSecondAsInt());
}
@Test
public void testFromSymbol()
{
assertEquals(DataRateSpec.DataRateUnit.fromSymbol("B/s"), DataRateSpec.DataRateUnit.BYTES_PER_SECOND);
assertEquals(DataRateSpec.DataRateUnit.fromSymbol("KiB/s"), DataRateSpec.DataRateUnit.KIBIBYTES_PER_SECOND);
assertEquals(DataRateSpec.DataRateUnit.fromSymbol("MiB/s"), DataRateSpec.DataRateUnit.MEBIBYTES_PER_SECOND);
assertThatThrownBy(() -> DataRateSpec.DataRateUnit.fromSymbol("n"))
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Unsupported data rate unit: n");
}
@Test
public void testInvalidInputs()
{
assertThatThrownBy(() -> new DataRateSpec("10")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid bit rate: 10");
assertThatThrownBy(() -> new DataRateSpec("-10b/s")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid bit rate: -10b/s");
assertThatThrownBy(() -> new DataRateSpec("10xb/s")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid bit rate: 10xb/s");
assertThatThrownBy(() -> new DataRateSpec("9223372036854775809B/s")
.toBytesPerSecond()).isInstanceOf(NumberFormatException.class)
.hasMessageContaining("For input string: \"9223372036854775809\"");
}
@Test
public void testEquals()
{
assertEquals(new DataRateSpec("10B/s"), new DataRateSpec("10B/s"));
assertEquals(new DataRateSpec("10KiB/s"), new DataRateSpec("10240B/s"));
assertEquals(new DataRateSpec("10240B/s"), new DataRateSpec("10KiB/s"));
assertEquals(DataRateSpec.inMebibytesPerSecond(Long.MAX_VALUE), DataRateSpec.inMebibytesPerSecond(Long.MAX_VALUE));
assertNotEquals(DataRateSpec.inMebibytesPerSecond(Long.MAX_VALUE), DataRateSpec.inBytesPerSecond(Long.MAX_VALUE));
assertNotEquals(new DataRateSpec("0KiB/s"), new DataRateSpec("10MiB/s"));
}
@Test
public void thereAndBack()
{
Gen<DataRateSpec.DataRateUnit> unitGen = SourceDSL.arbitrary().enumValues(DataRateSpec.DataRateUnit.class);
Gen<Long> valueGen = SourceDSL.longs().between(0, Long.MAX_VALUE);
qt().forAll(valueGen, unitGen).check((value, unit) -> {
DataRateSpec there = new DataRateSpec(value, unit);
DataRateSpec back = new DataRateSpec(there.toString());
DataRateSpec BACK = new DataRateSpec(there.toString());
return there.equals(back) && there.equals(BACK);
});
}
@Test
public void eq()
{
qt().forAll(gen(), gen()).check((a, b) -> a.equals(b) == b.equals(a));
}
@Test
public void eqAndHash()
{
qt().forAll(gen(), gen()).check((a, b) -> !a.equals(b) || a.hashCode() == b.hashCode());
}
private static Gen<DataRateSpec> gen()
{
Gen<DataRateSpec.DataRateUnit> unitGen = SourceDSL.arbitrary().enumValues(DataRateSpec.DataRateUnit.class);
Gen<Long> valueGen = SourceDSL.longs().between(0, Long.MAX_VALUE);
Gen<DataRateSpec> gen = rs -> new DataRateSpec(valueGen.generate(rs), unitGen.generate(rs));
return gen.describedAs(DataRateSpec::toString);
}
}

View File

@ -0,0 +1,141 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.config;
import java.util.Locale;
import org.junit.Test;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.quicktheories.core.Gen;
import org.quicktheories.generators.SourceDSL;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.*;
import static org.quicktheories.QuickTheory.qt;
public class DataStorageSpecTest
{
@Test
public void testConversions()
{
assertEquals(10, new DataStorageSpec("10B").toBytes());
assertEquals(10240, new DataStorageSpec("10KiB").toBytes());
assertEquals(0, new DataStorageSpec("10KiB").toMebibytes());
assertEquals(10240, new DataStorageSpec("10MiB").toKibibytes());
assertEquals(10485760, new DataStorageSpec("10MiB").toBytes());
}
@Test
public void testOverflowingDuringConversion()
{
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807B").toBytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807B").toBytesAsInt());
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807KiB").toBytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807KiB").toBytesAsInt());
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toBytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toBytesAsInt());
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toBytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toBytesAsInt());
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807KiB").toKibibytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807KiB").toKibibytesAsInt());
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toKibibytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toKibibytesAsInt());
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toKibibytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toKibibytesAsInt());
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toMebibytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toMebibytesAsInt());
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toMebibytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toMebibytesAsInt());
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toGibibytes());
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toGibibytesAsInt());
}
@Test
public void testFromSymbol()
{
assertEquals(DataStorageSpec.DataStorageUnit.fromSymbol("B"), DataStorageSpec.DataStorageUnit.BYTES);
assertEquals(DataStorageSpec.DataStorageUnit.fromSymbol("KiB"), DataStorageSpec.DataStorageUnit.KIBIBYTES);
assertEquals(DataStorageSpec.DataStorageUnit.fromSymbol("MiB"), DataStorageSpec.DataStorageUnit.MEBIBYTES);
assertEquals(DataStorageSpec.DataStorageUnit.fromSymbol("GiB"), DataStorageSpec.DataStorageUnit.GIBIBYTES);
assertThatThrownBy(() -> DataStorageSpec.DataStorageUnit.fromSymbol("n"))
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Unsupported data storage unit: n");
}
@Test
public void testInvalidInputs()
{
assertThatThrownBy(() -> new DataStorageSpec("10")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid data storage: 10");
assertThatThrownBy(() -> new DataStorageSpec("-10bps")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid data storage: -10bps");
assertThatThrownBy(() -> new DataStorageSpec("-10b")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid data storage: -10b");
assertThatThrownBy(() -> new DataStorageSpec("10HG")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid data storage: 10HG");
assertThatThrownBy(() -> new DataStorageSpec("9223372036854775809B")
.toBytes()).isInstanceOf(NumberFormatException.class)
.hasMessageContaining("For input string: \"9223372036854775809\"");
}
@Test
public void testEquals()
{
assertEquals(new DataStorageSpec("10B"), new DataStorageSpec("10B"));
assertEquals(new DataStorageSpec("10KiB"), new DataStorageSpec("10240B"));
assertEquals(new DataStorageSpec("10240B"), new DataStorageSpec("10KiB"));
assertEquals(DataStorageSpec.inMebibytes(Long.MAX_VALUE), DataStorageSpec.inMebibytes(Long.MAX_VALUE));
assertNotEquals(DataStorageSpec.inMebibytes(Long.MAX_VALUE), DataStorageSpec.inKibibytes(Long.MAX_VALUE));
assertNotEquals(DataStorageSpec.inMebibytes(Long.MAX_VALUE), DataStorageSpec.inBytes(Long.MAX_VALUE));
assertNotEquals(new DataStorageSpec("0MiB"), new DataStorageSpec("10KiB"));
}
@Test
public void thereAndBack()
{
qt().forAll(gen()).check(there -> {
DataStorageSpec back = new DataStorageSpec(there.toString());
DataStorageSpec BACK = new DataStorageSpec(there.toString().toUpperCase(Locale.ROOT).replace("I", "i"));
return there.equals(back) && there.equals(BACK);
});
}
@Test
public void eq()
{
qt().forAll(gen(), gen()).check((a, b) -> a.equals(b) == b.equals(a));
}
@Test
public void eqAndHash()
{
qt().forAll(gen(), gen()).check((a, b) -> !a.equals(b) || a.hashCode() == b.hashCode());
}
private static Gen<DataStorageSpec> gen()
{
Gen<DataStorageSpec.DataStorageUnit> unitGen = SourceDSL.arbitrary().enumValues(DataStorageSpec.DataStorageUnit.class);
Gen<Long> valueGen = SourceDSL.longs().between(0, Long.MAX_VALUE);
Gen<DataStorageSpec> gen = rs -> new DataStorageSpec(valueGen.generate(rs), unitGen.generate(rs));
return gen.describedAs(DataStorageSpec::toString);
}
}

View File

@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.config;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.quicktheories.core.Gen;
import org.quicktheories.generators.SourceDSL;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.*;
import static org.quicktheories.QuickTheory.qt;
public class DurationSpecTest
{
@Test
public void testConversions()
{
assertEquals(10L, new DurationSpec("10s").toSeconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toSecondsAsInt());
assertEquals(10000, new DurationSpec("10s").toMilliseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toMillisecondsAsInt());
assertEquals(0, new DurationSpec("10s").toMinutes());
assertEquals(10, new DurationSpec("10m").toMinutes());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toMinutesAsInt());
assertEquals(600000, new DurationSpec("10m").toMilliseconds());
assertEquals(600, new DurationSpec("10m").toSeconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toSecondsAsInt());
assertEquals(DurationSpec.inDoubleMilliseconds(0.7), new DurationSpec("1ms"));
assertEquals(DurationSpec.inDoubleMilliseconds(0.33), new DurationSpec("0ms"));
assertEquals(DurationSpec.inDoubleMilliseconds(0.333), new DurationSpec("0ms"));
}
@Test
public void testFromSymbol()
{
assertEquals(DurationSpec.fromSymbol("ms"), TimeUnit.MILLISECONDS);
assertEquals(DurationSpec.fromSymbol("d"), TimeUnit.DAYS);
assertEquals(DurationSpec.fromSymbol("h"), TimeUnit.HOURS);
assertEquals(DurationSpec.fromSymbol("m"), TimeUnit.MINUTES);
assertEquals(DurationSpec.fromSymbol("s"), TimeUnit.SECONDS);
assertEquals(DurationSpec.fromSymbol("us"), TimeUnit.MICROSECONDS);
assertEquals(DurationSpec.fromSymbol("µs"), TimeUnit.MICROSECONDS);
assertEquals(DurationSpec.fromSymbol("ns"), TimeUnit.NANOSECONDS);
assertThatThrownBy(() -> DurationSpec.fromSymbol("n")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Unsupported time unit: n");
}
@Test
public void testGetSymbol()
{
assertEquals(DurationSpec.getSymbol(TimeUnit.MILLISECONDS), "ms");
assertEquals(DurationSpec.getSymbol(TimeUnit.DAYS), "d");
assertEquals(DurationSpec.getSymbol(TimeUnit.HOURS), "h");
assertEquals(DurationSpec.getSymbol(TimeUnit.MINUTES), "m");
assertEquals(DurationSpec.getSymbol(TimeUnit.SECONDS), "s");
assertEquals(DurationSpec.getSymbol(TimeUnit.MICROSECONDS), "us");
assertEquals(DurationSpec.getSymbol(TimeUnit.NANOSECONDS), "ns");
}
@Test
public void testInvalidInputs()
{
assertThatThrownBy(() -> new DurationSpec("10")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10");
assertThatThrownBy(() -> new DurationSpec("-10s")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: -10s");
assertThatThrownBy(() -> new DurationSpec("10xd")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10xd");
assertThatThrownBy(() -> new DurationSpec("0.333555555ms")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 0.333555555ms");
}
@Test
public void testEquals()
{
assertEquals(new DurationSpec("10s"), new DurationSpec("10s"));
assertEquals(new DurationSpec("10s"), new DurationSpec("10000ms"));
assertEquals(new DurationSpec("10000ms"), new DurationSpec("10s"));
assertEquals(DurationSpec.inMinutes(Long.MAX_VALUE), DurationSpec.inMinutes(Long.MAX_VALUE));
assertEquals(new DurationSpec("4h"), new DurationSpec("14400s"));
assertEquals(DurationSpec.inSecondsString("14400"), new DurationSpec("14400s"));
assertEquals(DurationSpec.inSecondsString("4h"), new DurationSpec("14400s"));
assertEquals(DurationSpec.inSecondsString("14400s"), new DurationSpec("14400s"));
assertEquals(DurationSpec.inHours(Long.MAX_VALUE),DurationSpec.inHours(Long.MAX_VALUE));
assertNotEquals(DurationSpec.inMinutes(Long.MAX_VALUE), DurationSpec.inMilliseconds(Long.MAX_VALUE));
assertNotEquals(new DurationSpec("0m"), new DurationSpec("10ms"));
}
@Test
public void thereAndBack()
{
Gen<TimeUnit> unitGen = SourceDSL.arbitrary().enumValues(TimeUnit.class);
Gen<Long> valueGen = SourceDSL.longs().between(0, Long.MAX_VALUE);
qt().forAll(valueGen, unitGen).check((value, unit) -> {
DurationSpec there = new DurationSpec(value, unit);
DurationSpec back = new DurationSpec(there.toString());
return there.equals(back);
});
}
@Test
public void testOverflowingDuringConversion()
{
// we are heavily dependent on the Java TimeUnit for our configuration of type duration. We want to be sure
// that any regression in handlining overflow will be caught quickly on our end
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807ns").toNanoseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807ns").toNanosecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807ms").toNanoseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807ms").toNanosecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807s").toNanoseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toNanosecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807m").toNanoseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807m").toNanosecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toNanoseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toNanosecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807ms").toMilliseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807ms").toMillisecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807s").toMilliseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toMillisecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807m").toMilliseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807m").toMillisecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toMilliseconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toMillisecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807s").toSeconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toSecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807m").toSeconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807m").toSecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toSeconds());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toSecondsAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807m").toMinutes());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807m").toMinutesAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toMinutes());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toMinutesAsInt());
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toHours());
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toHoursAsInt());
}
}