Thank you for your interest in contributing to GenServerVirtualTime! This document provides guidelines and instructions for contributing.
- Code of Conduct
- Getting Started
- Development Setup
- Development Workflow
- Code Guidelines
- Testing Guidelines
- Documentation Guidelines
- Submitting Changes
- Release Process
This project follows a simple code of conduct:
- Be respectful and inclusive
- Assume good intentions
- Provide constructive feedback
- Focus on the best outcome for the community
- Elixir 1.14 or later
- OTP 25 or later
- Git
- A GitHub account
- Check existing issues: Look for issues labeled
good first issueorhelp wanted - Report bugs: Found a bug? Open an issue first to discuss
- Propose features: Have an idea? Open an issue to discuss before implementing
- Improve documentation: Documentation improvements are always welcome
-
Fork and clone the repository:
git clone https://github.com/your-username/gen_server_virtual_time.git cd gen_server_virtual_time -
Install dependencies:
mix deps.get
-
Verify the setup:
mix test mix docs -
Create a branch:
git checkout -b feature/my-new-feature # or git checkout -b fix/issue-123
# Run all tests (default - excludes slow and diagram_generation)
mix test
# Fast tests only (excludes slow, diagram_generation, ridiculous)
mix test --exclude slow --exclude diagram_generation --exclude ridiculous
# Slow tests only
mix test --include slow
# Diagram generation tests
mix test --include diagram_generation
# Ridiculous tests (very long-running)
mix test --include ridiculous
# Run specific test file
mix test test/virtual_clock_test.exs
# Run with coverage
mix test --cover
# Run with detailed coverage
mix coveralls.html
open cover/excoveralls.html
# Combined coverage (all test types)
./scripts/coverage_combined.sh
# Run failed tests
mix test --failed
# Run tests matching a pattern
mix test --only focus
# Run tests with specific tag
mix test --only integrationThe project uses a tag-based testing system for different test categories:
:slow- Long-running tests (time simulations, complex scenarios):diagram_generation- Tests that generate Mermaid diagrams and reports:ridiculous- Extremely long-running tests (century simulations, stress tests):tmp_dir- Tests requiring temporary directories:integration- Integration tests with external systems
Default behavior: mix test excludes :slow and :diagram_generation tags
for fast development feedback.
# Format code
mix format
# Check formatting
mix format --check-formatted
# Run Credo (code analysis)
mix credo --strict
# Run Dialyzer (type checking)
mix dialyzer
# Run all quality checks
mix format --check-formatted && mix credo --strict && mix dialyzer# Generate documentation
mix docs
# Open documentation in browser
open doc/index.html# Run demo scripts
mix run examples/demo.exs
mix run examples/advanced_demo.exs
mix run examples/omnetpp_demo.exs- Follow the Elixir Style Guide
- Use
mix formatto automatically format code - Maximum line length: 120 characters
- Use meaningful variable and function names
defmodule MyModule do
@moduledoc """
Brief description of the module.
## Examples
iex> MyModule.function()
:ok
"""
# Behaviors and use statements
use SomeBehavior
# Aliases
alias Some.Module
# Module attributes
@default_timeout 5000
# Types
@type t :: %__MODULE__{}
# Public functions
def public_function do
# ...
end
# Private functions
defp private_function do
# ...
end
end- Every public module must have
@moduledoc - Every public function must have
@doc - Include examples in documentation using doctests
- Use
@specfor all public functions
Example:
@doc """
Advances the virtual clock by the specified duration.
Returns `:ok` on success.
## Parameters
* `clock` - The PID of the virtual clock
* `duration` - Duration in milliseconds to advance
## Examples
iex> {:ok, clock} = VirtualClock.start_link()
iex> VirtualClock.advance(clock, 1000)
:ok
"""
@spec advance(pid(), non_neg_integer()) :: :ok
def advance(clock, duration) when is_integer(duration) and duration >= 0 do
# Implementation
end- Use pattern matching over try/catch when possible
- Return
{:ok, result}or{:error, reason}tuples for operations that can fail - Use
!suffix for functions that raise exceptions - Document all possible error returns
- Write tests for all public functions
- Use descriptive test names
- One assertion per test when possible
- Test both success and failure cases
Example:
describe "advance/2" do
test "advances clock by specified duration" do
{:ok, clock} = VirtualClock.start_link()
assert VirtualClock.now(clock) == 0
VirtualClock.advance(clock, 1000)
assert VirtualClock.now(clock) == 1000
end
test "handles negative duration" do
{:ok, clock} = VirtualClock.start_link()
assert {:error, :invalid_duration} = VirtualClock.advance(clock, -100)
end
end- Group related tests using
describeblocks - Use descriptive test names that explain the behavior
- Keep tests focused and independent
- Use setup blocks to reduce duplication
- Aim for >80% test coverage
- All public functions must have tests
- Test edge cases and error conditions
- Don't test private functions directly
# Run tests matching a pattern
mix test --only focus
# Run failed tests
mix test --failed
# Run tests with specific tag
mix test --only integration- Keep installation instructions up-to-date
- Add examples for new features
- Update feature list
- Keep API reference current
- Add entries under
[Unreleased]section - Use categories: Added, Changed, Deprecated, Removed, Fixed, Security
- Include ticket/issue numbers when relevant
Example:
## [Unreleased]
### Added
- New `burst` send pattern for actor simulation (#123)
### Fixed
- Virtual clock race condition in concurrent scenarios (#124)- Start with a brief one-line summary
- Provide a detailed explanation
- Include usage examples
- Document all options and callbacks
-
Run all checks:
./scripts/prepare_release.sh
-
Update documentation:
- Add/update module docs
- Update README if needed
- Add CHANGELOG entry
-
Write/update tests:
- Add tests for new functionality
- Update existing tests if behavior changed
- Ensure all tests pass
-
Create a pull request:
- Use a clear, descriptive title
- Fill out the PR template completely
- Reference related issues
-
PR title format:
feat: Add burst send pattern to actor simulationfix: Resolve race condition in VirtualClockdocs: Update installation instructionstest: Add tests for edge cases in send_afterrefactor: Simplify VirtualClock state managementperf: Optimize event queue processing
-
Review process:
- Address review comments promptly
- Keep the PR updated with main branch
- Be open to feedback and suggestions
-
Merging:
- PRs require at least one approval
- All CI checks must pass
- Maintainers will merge approved PRs
- Use clear, descriptive commit messages
- Start with a verb in present tense (Add, Fix, Update, etc.)
- Reference issue numbers when applicable
- Keep commits focused and atomic
Good commit messages:
Add burst send pattern to ActorSimulation
Implement burst pattern that sends N messages every interval.
Useful for modeling bursty traffic patterns.
Fixes #123
Fix race condition in VirtualClock.advance/2
The advance function could race with scheduled events,
causing events to be processed out of order. This change
adds proper synchronization.
Related to #124
Maintainers handle releases, but understanding the process helps with contributions:
-
Version bump:
./scripts/bump_version.sh patch # or minor, major -
Update CHANGELOG:
- Move unreleased items to new version section
- Add release date
- Review and organize entries
-
Create release:
git push origin main git push origin --tags
-
Verify:
- Check GitHub Actions workflows pass
- Verify package on Hex.pm
- Verify docs on HexDocs.pm
- Chat: Open a discussion on GitHub
- Issues: Open an issue for bugs or feature requests
- Email: Contact maintainers directly for sensitive issues
By contributing, you agree that your contributions will be licensed under the MIT License.
Your contributions make this project better for everyone. Thank you for taking the time to contribute! 🎉