using FsCheck;
using FsCheck.Xunit;
using WorkCameraExport.Forms;
using Xunit;
namespace WorkCameraExport.Tests
{
///
/// ImageViewerForm 属性测试
/// Feature: work-camera-2.0.1, Property 2: 图片轮播索引边界
/// Validates: Requirements 3.6
///
public class ImageViewerPropertyTests
{
///
/// Property 2: 图片轮播索引边界
/// For any 图片列表,当前索引应始终在 [0, 图片数量-1] 范围内。
/// 测试:WrapIndex 方法应始终返回有效范围内的索引
///
[Property(MaxTest = 100)]
public Property WrapIndex_ShouldAlwaysReturnValidIndex()
{
return Prop.ForAll(
Arb.From(),
Arb.From(),
(index, countGen) =>
{
var count = countGen.Get;
var result = ImageViewerForm.WrapIndex(index, count);
return (result >= 0 && result < count)
.Label($"WrapIndex({index}, {count}) = {result} should be in [0, {count - 1}]");
});
}
///
/// Property 2 扩展: 点击下一张时,如果当前是最后一张则循环到第一张
///
[Property(MaxTest = 100)]
public Property WrapIndex_NextFromLast_ShouldWrapToFirst()
{
return Prop.ForAll(
Arb.From(),
(countGen) =>
{
var count = countGen.Get;
var lastIndex = count - 1;
var nextIndex = lastIndex + 1;
var result = ImageViewerForm.WrapIndex(nextIndex, count);
return (result == 0)
.Label($"WrapIndex({nextIndex}, {count}) should be 0 (wrap to first), got {result}");
});
}
///
/// Property 2 扩展: 点击上一张时,如果当前是第一张则循环到最后一张
///
[Property(MaxTest = 100)]
public Property WrapIndex_PreviousFromFirst_ShouldWrapToLast()
{
return Prop.ForAll(
Arb.From(),
(countGen) =>
{
var count = countGen.Get;
var previousIndex = -1;
var result = ImageViewerForm.WrapIndex(previousIndex, count);
return (result == count - 1)
.Label($"WrapIndex(-1, {count}) should be {count - 1} (wrap to last), got {result}");
});
}
///
/// Property 2 扩展: 连续多次 next 操作应正确循环
///
[Property(MaxTest = 100)]
public Property WrapIndex_MultipleNextOperations_ShouldCycleCorrectly()
{
return Prop.ForAll(
Arb.From(),
Arb.From(),
(countGen, stepsGen) =>
{
var count = (countGen.Get % 20) + 1; // 1-20 张图片
var steps = stepsGen.Get % 100; // 最多 100 步
var currentIndex = 0;
for (int i = 0; i < steps; i++)
{
currentIndex = ImageViewerForm.WrapIndex(currentIndex + 1, count);
// 每一步都应该在有效范围内
if (currentIndex < 0 || currentIndex >= count)
{
return false.Label($"Index {currentIndex} out of range after {i + 1} steps");
}
}
return true.Label($"All {steps} next operations stayed in valid range [0, {count - 1}]");
});
}
///
/// Property 2 扩展: 连续多次 previous 操作应正确循环
///
[Property(MaxTest = 100)]
public Property WrapIndex_MultiplePreviousOperations_ShouldCycleCorrectly()
{
return Prop.ForAll(
Arb.From(),
Arb.From(),
(countGen, stepsGen) =>
{
var count = (countGen.Get % 20) + 1; // 1-20 张图片
var steps = stepsGen.Get % 100; // 最多 100 步
var currentIndex = 0;
for (int i = 0; i < steps; i++)
{
currentIndex = ImageViewerForm.WrapIndex(currentIndex - 1, count);
// 每一步都应该在有效范围内
if (currentIndex < 0 || currentIndex >= count)
{
return false.Label($"Index {currentIndex} out of range after {i + 1} steps");
}
}
return true.Label($"All {steps} previous operations stayed in valid range [0, {count - 1}]");
});
}
///
/// Property 2 扩展: 完整循环后应回到起始位置
///
[Property(MaxTest = 100)]
public Property WrapIndex_FullCycle_ShouldReturnToStart()
{
return Prop.ForAll(
Arb.From(),
(countGen) =>
{
var count = (countGen.Get % 50) + 1; // 1-50 张图片
var currentIndex = 0;
// 执行 count 次 next 操作
for (int i = 0; i < count; i++)
{
currentIndex = ImageViewerForm.WrapIndex(currentIndex + 1, count);
}
return (currentIndex == 0)
.Label($"After {count} next operations, index should be 0, got {currentIndex}");
});
}
///
/// Property 2 扩展: 空列表或无效 count 应返回 0
///
[Fact]
public void WrapIndex_ZeroOrNegativeCount_ShouldReturnZero()
{
Assert.Equal(0, ImageViewerForm.WrapIndex(0, 0));
Assert.Equal(0, ImageViewerForm.WrapIndex(5, 0));
Assert.Equal(0, ImageViewerForm.WrapIndex(-5, 0));
Assert.Equal(0, ImageViewerForm.WrapIndex(0, -1));
Assert.Equal(0, ImageViewerForm.WrapIndex(5, -5));
}
///
/// Property 2 扩展: 单张图片时索引应始终为 0
///
[Property(MaxTest = 100)]
public Property WrapIndex_SingleImage_ShouldAlwaysReturnZero()
{
return Prop.ForAll(
Arb.From(),
(index) =>
{
var result = ImageViewerForm.WrapIndex(index, 1);
return (result == 0)
.Label($"WrapIndex({index}, 1) should always be 0, got {result}");
});
}
///
/// Property 2 扩展: 大负数索引应正确处理
///
[Property(MaxTest = 100)]
public Property WrapIndex_LargeNegativeIndex_ShouldWrapCorrectly()
{
return Prop.ForAll(
Arb.From(),
Arb.From(),
(countGen, multiplierGen) =>
{
var count = (countGen.Get % 20) + 1;
var multiplier = multiplierGen.Get;
var largeNegativeIndex = -(count * multiplier);
var result = ImageViewerForm.WrapIndex(largeNegativeIndex, count);
return (result >= 0 && result < count)
.Label($"WrapIndex({largeNegativeIndex}, {count}) = {result} should be in [0, {count - 1}]");
});
}
}
}