Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ jobs:
uses: FoundatioFx/Foundatio/.github/workflows/build-workflow.yml@main
with:
org: exceptionless
new-test-runner: true
secrets:
NUGET_KEY: ${{ secrets.NUGET_KEY }}
FEEDZ_KEY: ${{ secrets.FEEDZ_KEY }}
39 changes: 39 additions & 0 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: "Code scanning - action"

on:
schedule:
- cron: '0 1 * * 2'

jobs:
CodeQL-Build:

runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2

# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: csharp

- name: Setup .NET Core
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- name: Build
run: dotnet build Exceptionless.RandomData.slnx --configuration Release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
26 changes: 26 additions & 0 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: "Copilot Setup"

on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml

jobs:
copilot-setup-steps:
runs-on: ubuntu-latest

permissions:
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Setup .NET Core
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
138 changes: 138 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Agent Guidelines for Exceptionless.RandomData

You are an expert .NET engineer working on Exceptionless.RandomData, a focused utility library for generating random data useful in unit tests and data seeding. The library provides methods for generating random integers, longs, doubles, decimals, booleans, strings, words, sentences, paragraphs, dates, enums, IP addresses, versions, and coordinates. It also includes an `EnumerableExtensions.Random<T>()` extension method to pick a random element from any collection.

**Craftsmanship Mindset**: Every line of code should be intentional, readable, and maintainable. Write code you'd be proud to have reviewed by senior engineers. Prefer simplicity over cleverness. When in doubt, favor explicitness and clarity.

## Repository Overview

Exceptionless.RandomData provides random data generation utilities for .NET applications:

- **Numeric** - `GetInt`, `GetLong`, `GetDouble`, `GetDecimal` with optional min/max ranges
- **Boolean** - `GetBool` with configurable probability (0-100%)
- **String** - `GetString`, `GetAlphaString`, `GetAlphaNumericString` with configurable length and allowed character sets
- **Text** - `GetWord`, `GetWords`, `GetTitleWords`, `GetSentence`, `GetParagraphs` with lorem ipsum-style words and optional HTML output
- **DateTime** - `GetDateTime`, `GetDateTimeOffset`, `GetTimeSpan` with optional start/end ranges
- **Enum** - `GetEnum<T>()` to pick a random enum value (constrained to `struct, Enum`)
- **Network** - `GetIp4Address` for random IPv4 addresses
- **Version** - `GetVersion` for random version strings with optional min/max
- **Coordinate** - `GetCoordinate` for random lat/lng pairs
- **Collection** - `EnumerableExtensions.Random<T>()` to pick a random element from any `IEnumerable<T>`

Design principles: **simplicity**, **thread safety** (uses `Random.Shared`), **cryptographic quality strings** (uses `RandomNumberGenerator`), **modern .NET features** (targeting net8.0/net10.0).

## Quick Start

```bash
# Build
dotnet build Exceptionless.RandomData.slnx

# Test
dotnet run --project test/Exceptionless.RandomData.Tests -f net8.0

# Format code
dotnet format Exceptionless.RandomData.slnx
```

## Project Structure

```text
src
└── Exceptionless.RandomData
└── RandomData.cs # All random data generation + EnumerableExtensions
test
└── Exceptionless.RandomData.Tests
├── RandomDataTests.cs # Unit tests
└── Properties
└── AssemblyInfo.cs # Disables test parallelization
```

## Coding Standards

### Style & Formatting

- Follow `.editorconfig` rules (file-scoped namespaces, K&R braces)
- Follow [Microsoft C# conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions)
- Use `String.`/`Int32.`/`Char.` for static method access per `.editorconfig` `dotnet_style_predefined_type_for_member_access = false`
- Run `dotnet format` to auto-format code
- Match existing file style; minimize diffs
- No code comments unless necessary—code should be self-explanatory

### Code Quality

- Write complete, runnable code—no placeholders, TODOs, or `// existing code...` comments
- Use modern C# features available in **net8.0/net10.0**
- **Nullable reference types** are enabled—annotate nullability correctly, don't suppress warnings without justification
- **ImplicitUsings** are enabled—don't add `using System;`, `using System.Collections.Generic;`, etc.
- Follow SOLID, DRY principles; remove unused code and parameters
- Clear, descriptive naming; prefer explicit over clever

### Modern .NET Idioms

- **Guard APIs**: Use `ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual()`, `ArgumentOutOfRangeException.ThrowIfLessThan()`, `ArgumentOutOfRangeException.ThrowIfGreaterThan()`, `ArgumentNullException.ThrowIfNull()`, `ArgumentException.ThrowIfNullOrEmpty()` instead of manual checks
- **`Random.Shared`**: Use `Random.Shared` instead of `new Random()` for thread-safe random number generation
- **`RandomNumberGenerator.Fill()`**: Use the static method instead of `RandomNumberGenerator.Create()` + disposal
- **Collection expressions**: Use `[...]` syntax for array initialization
- **`Span<T>`**: Use `stackalloc` and span-based APIs to avoid allocations in hot paths
- **Expression-bodied members**: Use for single-expression methods
- **`Math.Clamp`**: Use instead of separate `Math.Min`/`Math.Max` calls
- **Generic constraints**: Use `where T : struct, Enum` instead of runtime `typeof(T).IsEnum` checks
- **Pattern matching**: Use `is null` / `is not null` instead of `== null` / `!= null`

### Exceptions

- Use `ArgumentOutOfRangeException.ThrowIf*` guard APIs at method entry
- Use `ArgumentException` for invalid arguments that don't fit range checks
- Include parameter names via `nameof()` where applicable
- Fail fast: throw exceptions immediately for invalid arguments

## Making Changes

### Before Starting

1. **Gather context**: Read `RandomData.cs` and the test file to understand the full scope
2. **Research patterns**: Find existing usages of the code you're modifying
3. **Understand completely**: Know the problem, side effects, and edge cases before coding
4. **Plan the approach**: Choose the simplest solution that satisfies all requirements

### While Coding

- **Minimize diffs**: Change only what's necessary, preserve formatting and structure
- **Preserve behavior**: Don't break existing functionality or change semantics unintentionally
- **Build incrementally**: Run `dotnet build` after each logical change to catch errors early
- **Test continuously**: Run tests frequently to verify correctness
- **Match style**: Follow the patterns in surrounding code exactly

### Validation

Before marking work complete, verify:

1. **Builds successfully**: `dotnet build Exceptionless.RandomData.slnx` exits with code 0
2. **All tests pass**: `dotnet run --project test/Exceptionless.RandomData.Tests -f net8.0` shows no failures
3. **No new warnings**: Check build output for new compiler warnings (warnings are treated as errors)
4. **API compatibility**: Public API changes are intentional and backward-compatible when possible
5. **Breaking changes flagged**: Clearly identify any breaking changes for review

## Testing

### Framework

- **xUnit v3** with **Microsoft Testing Platform** as the test runner
- Test parallelization is disabled via `Properties/AssemblyInfo.cs`

### Running Tests

```bash
# All tests, both TFMs
dotnet run --project test/Exceptionless.RandomData.Tests -f net8.0
dotnet run --project test/Exceptionless.RandomData.Tests -f net10.0
```

### Note on Namespace Conflict

The test project uses `<RootNamespace>Exceptionless.Tests</RootNamespace>` to avoid a namespace conflict where the xUnit v3 MTP source generator creates a namespace `Exceptionless.RandomData.Tests` that shadows the `Exceptionless.RandomData` class. The test code uses `using Xunit;` and references `RandomData.*` methods directly since the `Exceptionless` namespace is accessible from within `Exceptionless.Tests`.

## Resources

- [README.md](README.md) - Overview and usage examples
- [NuGet Package](https://www.nuget.org/packages/Exceptionless.RandomData/)
103 changes: 87 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,103 @@
[![Discord](https://img.shields.io/discord/715744504891703319)](https://discord.gg/6HxgFCx)
[![Donate](https://img.shields.io/badge/donorbox-donate-blue.svg)](https://donorbox.org/exceptionless?recurring=true)

Utility class to easily generate random data. This makes generating good unit test data a breeze!
A utility library for generating random data in .NET. Makes generating realistic test data a breeze. Targets **net8.0** and **net10.0**.

## Getting Started (Development)
## Getting Started

[This package](https://www.nuget.org/packages/Exceptionless.RandomData/) can be installed via the [NuGet package manager](https://docs.nuget.org/consume/Package-Manager-Dialog). If you need help, please contact us via in-app support or [open an issue](https://github.com/exceptionless/Exceptionless.RandomData/issues/new). Were always here to help if you have any questions!
[This package](https://www.nuget.org/packages/Exceptionless.RandomData/) can be installed via the [NuGet package manager](https://docs.nuget.org/consume/Package-Manager-Dialog). If you need help, please contact us via in-app support or [open an issue](https://github.com/exceptionless/Exceptionless.RandomData/issues/new). We're always here to help if you have any questions!

1. You will need to have [Visual Studio Code](https://code.visualstudio.com/) installed.
2. Open the root folder.
```
dotnet add package Exceptionless.RandomData
```

## Usage

All methods are on the static `RandomData` class in the `Exceptionless` namespace.

### Numbers

```csharp
using Exceptionless;

int value = RandomData.GetInt(1, 100);
long big = RandomData.GetLong(0, 1_000_000);
double d = RandomData.GetDouble(0.0, 1.0);
decimal m = RandomData.GetDecimal(1, 500);
```

### Booleans

```csharp
using Exceptionless;

bool coin = RandomData.GetBool();
bool likely = RandomData.GetBool(chance: 80); // 80% chance of true
```

### Strings

```csharp
using Exceptionless;

string random = RandomData.GetString(minLength: 5, maxLength: 20);
string alpha = RandomData.GetAlphaString(10, 10);
string alphaNum = RandomData.GetAlphaNumericString(8, 16);
```

## Using RandomData
### Words, Sentences, and Paragraphs

Below is a small sample of what you can do, so check it out!
```csharp
using Exceptionless;

string word = RandomData.GetWord();
string title = RandomData.GetTitleWords(minWords: 3, maxWords: 6);
string sentence = RandomData.GetSentence(minWords: 5, maxWords: 15);
string text = RandomData.GetParagraphs(count: 2, minSentences: 3, maxSentences: 10);
string html = RandomData.GetParagraphs(count: 2, html: true);
```

### Dates and Times

```csharp
using Exceptionless;

DateTime date = RandomData.GetDateTime();
DateTime recent = RandomData.GetDateTime(start: DateTime.UtcNow.AddDays(-30), end: DateTime.UtcNow);
DateTimeOffset dto = RandomData.GetDateTimeOffset();
TimeSpan span = RandomData.GetTimeSpan(min: TimeSpan.FromMinutes(1), max: TimeSpan.FromHours(2));
```

### Enums

```csharp
private int[] _numbers = new[] { 1, 2, 3, 4, 5 };
using Exceptionless;

private enum _days {
Monday,
Tuesday
}
DayOfWeek day = RandomData.GetEnum<DayOfWeek>();
```

### Network and Versioning

```csharp
using Exceptionless;

string ip = RandomData.GetIp4Address(); // e.g. "192.168.4.12"
string coord = RandomData.GetCoordinate(); // e.g. "45.123,-90.456"
string version = RandomData.GetVersion("1.0", "5.0");
```

### Pick Random from Collection

The `Random<T>()` extension method picks a random element from any `IEnumerable<T>`:

```csharp
using Exceptionless;

int value = RandomData.GetInt(1, 5);
// or
value = _numbers.Random();
int[] numbers = [1, 2, 3, 4, 5];
int picked = numbers.Random();

var day = RandomData.GetEnum<_days>();
string[] names = ["Alice", "Bob", "Charlie"];
string? name = names.Random();
```

## Thanks to all the people who have contributed
Expand Down
14 changes: 8 additions & 6 deletions build/common.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<Project>

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Product>Exceptionless RandomData Generator</Product>
<Description>Exceptionless RandomData Generator</Description>
<PackageProjectUrl>https://github.com/exceptionless/Exceptionless.RandomData</PackageProjectUrl>
Expand All @@ -10,9 +12,9 @@
<MinVerSkip Condition="'$(Configuration)' == 'Debug'">true</MinVerSkip>
<MinVerTagPrefix>v</MinVerTagPrefix>

<Copyright>Copyright (c) 2025 Exceptionless. All rights reserved.</Copyright>
<Copyright>Copyright © $([System.DateTime]::Now.ToString(yyyy)) Exceptionless. All rights reserved.</Copyright>
<Authors>Exceptionless</Authors>
<NoWarn>$(NoWarn);CS1591;NU1701</NoWarn>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<WarningsAsErrors>true</WarningsAsErrors>
<LangVersion>latest</LangVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand All @@ -31,9 +33,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
<PackageReference Include="AsyncFixer" Version="1.6.0" PrivateAssets="All" />
<PackageReference Include="MinVer" Version="5.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103" PrivateAssets="All"/>
<PackageReference Include="AsyncFixer" Version="2.1.0" PrivateAssets="All" />
<PackageReference Include="MinVer" Version="7.0.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 5 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"test": {
"runner": "Microsoft.Testing.Platform"
}
}
Loading