Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Documentation revamp #1146

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ per developer. CI usage for licensed developers is included.

Opensource projects have to acquire their free license per repository.

The license distribution happens through the `mutant-license` gem in mutants dependencies.
The license distribution happens through the `mutant-license` gem in mutant’s dependencies.
This gem is dynamically generated per licensee and comes with a unique license gem source
URL.

Expand All @@ -99,8 +99,7 @@ end

The mutant license gem contains metadata that allows mutant to verify licensed use.

For commercial licenses mutant checks the git commit author or the configured git email
to be in the set of licensed developers.
For commercial licenses mutant checks the git commit author or the configured git email to be in the set of licensed developers.

For opensource licenses mutant checks the git remotes against the licensed git repositories.
This allows the project maintainer to sign up and not bother collaborators with the details.
Expand Down Expand Up @@ -177,8 +176,7 @@ of their private time.
Additionally, the following features where sponsored by organizations:

* The `mutant-minitest` integration was sponsored by [Arkency](https://arkency.com/)
* Mutant's initial concurrency support was sponsored by an undisclosed company that does
currently not wish to be listed here.
* Mutant's initial concurrency support was sponsored by an undisclosed company that does currently not wish to be listed here.

### Legal

Expand Down
2 changes: 1 addition & 1 deletion docs/commercial-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ Covers 1 incident per quarter, with a max response time of 7 days.
Scope is limited to mutant not the application or infrastructure.

For support email [Markus Schirp](mailto:[email protected]?subject=Mutant%20Support).
Please email using the same domain as the roiginal license email or explain
Please email using the same domain as the original license email or explain
your connection to the license.
21 changes: 18 additions & 3 deletions docs/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ Subject

Mutant cannot emit mutations for some subjects.

* methods defined within a closure. For example, methods defined using `module_eval`, `class_eval`,
`define_method`, or `define_singleton_method`:
* methods defined within a closure. For example, methods defined using
`module_eval`, `class_eval`, `define_method`, or `define_singleton_method`:

```ruby
class Example
Expand All @@ -29,7 +29,7 @@ Mutant cannot emit mutations for some subjects.
end
```

* singleton methods not defined on a constant or `self`
* inline-style singleton methods not defined on a constant or on `self`

```ruby
class Foo
Expand All @@ -41,6 +41,21 @@ Mutant cannot emit mutations for some subjects.
end
```

* multiline-style singleton methods not defined on `self`
telyn marked this conversation as resolved.
Show resolved Hide resolved

```ruby
class Foo
class << self
def bar; end # ok
end
end

foo = "Hello"
class << foo
def greet!; end # cannot mutate
end
```

* methods defined with eval:

```ruby
Expand Down
2 changes: 1 addition & 1 deletion docs/mutant-rspec.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ To add mutant to your rspec code base you need to:

## Run through example

This uses [mbj/auom](https://github.com/mbj/auom) a small library that
This uses [mbj/auom](https://github.com/mbj/auom), a small library that
has 100% mutation coverage. Its tests execute very fast and do not have any IO
so its a good playground example to interact with.

Expand Down
89 changes: 77 additions & 12 deletions docs/nomenclature.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
Nomenclature
============

The following explains several nouns you may experience in mutant's documentation.
This document explains several nouns you may experience in mutant's documentation.
It's a good idea to familiarize yourself before moving on.

## Mutation Testing
The practice of systematically applying small changes one at a time to a codebase then re-running the (relevant) tests for each change. If the tests fail, then they cover the semantics of those changes. If the tests continue to pass with the changes in place, the tests do not cover the complete semantics of the changed parts of the codebase.

Each type of change (for example, “change `a && b` to `a`”) is known as a [Mutation Operator](#mutation-operator), and is applied to the pristine codebase - that is, the changes are not stacked up, but applied in isolation. The places in which the Mutation Operators can make their changes are called [Subjects](#subject). The changed Subject is then referred to as a [Mutation](#mutation).
Copy link
Sponsor Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Each type of change (for example, “change a && b to a”) is known as a ..."

This sounds like the change itself is a "Mutation Operator" but I think that would be a Mutation. You mean to be talking about kinds of changes but I think the example confuses it a bit because it is an instance of a mutation.


Mutation testing can be useful for suggesting semantic simplifications to your code, as well as highlighting gaps in your tests as you are writing them.

## AST

Acronym for [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)
and the level of abstraction mutant operates on.
In short, this is a representation of the structure and content of your code
stored in memory which mutant alters.

## Subject

Expand All @@ -18,25 +27,81 @@ Mutant currently supports the following subjects:
* Instance methods
* Singleton (class) methods

Other subjects (constants, class bodies for DSLs, ...) are possible but aren't
implemented in the OSS version.
Other subjects are possible (even project-specific subjects) but aren't implemented in the OSS version. Please get in touch with the authors. Some examples are:
* Constants
* Class bodies for DSLs

The more subjects that mutant can alter in your project, the more mutations it can create, and so the higher your confidence you can have that your tests cover the semantics of your application. Please get in touch if you require subjects beyond those implemented in Mutant already - support may be available in the commercial version.
Copy link
Sponsor Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This advises you to get in touch twice--feels slightly redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I had meant to supplant the first one with the latter 😅


## Mutation Operator
Copy link
Sponsor Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this was discussed in the discord or elsewhere, but this terminology seems slightly confusing. What if this heading was just something like "Kinds of Mutations".

Copy link
Contributor Author

@telyn telyn Dec 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it's confusing, and I think it's an unnecessary type of thing to have as its own heading. I am aware of some nomenclature-related discussions occurring in the discord but I've struggled to find any kind of conclusive decision-making.

I used it because it was used in the original document, but I think it may be better to have it as a sort-of "here are some types of mutations that can occur" sub-section in the "Mutations" section. What do you & @mbj think to that?


A transformation applied to the AST of a subject. Mutant knows the following high level operator classes (you will not be tested on your knowledge of these terms)

Please be aware that there is no reason to learn these terms (there’s no quiz at the end of this semester!) - we’re just providing them to give you some ideas of what high-level classes of changes mutant makes to your code. For a full list of all the Mutation Operators in mutant, see the code in the [meta directory](https://github.com/mbj/mutant/tree/master/meta).

* **Semantic Reduction**

This type of transformation replaces a piece of code which has somewhat complex semantics with one that has simpler semantics. To aid understanding, here are a couple of different sub-categories you could put them into.

* **Method call replacement** - for example, `#==` -> `#eql?` -> `#equal?`

`#==` commonly performs conversion between types in addition to checking equality, while `#eql?` tends to check only that the class and instance variables are equal. Therefore we would say that #== is semantically simpler.
telyn marked this conversation as resolved.
Show resolved Hide resolved

You could also think of a semantic reduction in these cases as an increase in “strictness” of the code. `#equal?` is a stricter equality test than `#eql?`, and so on

* **Code removal** - For example, `def my_method; do_something; end` -> `def my_method; end`
telyn marked this conversation as resolved.
Show resolved Hide resolved

We could argue[^1] that this category includes all of the following:

* Removing a particular expression, from the AST entirely
* Replacing an expression with `nil`, or `true`, or `false`, or other simple literals.
Copy link
Sponsor Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't tend to think of those as quite the same as "code removal" but maybe it belongs here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mm. That's true, it's more of an extreme semantic reduction, I suppose 😛

I think one thing that's missing from this whole section is a motivation for choosing to categorise things, as well as a motivation for these specific categories. I'm mostly using it as a way to structure the documentation to break it up into smaller more digestible pieces.

In my experience, when I run mutant on a subject and it reduces 2-3 lines of code to nil or self, I'm thinking "ah I just haven't specified those features in my tests", or "perhaps I could restructure that into its own method and test it individually". When I run it and it removes or reduces a smaller amount of code, I've found it's more likely that it's actually removing / reducing something that isn't necessary. This may just be a symptom of the fact my mutation testing has so far been limited to things I have built since setting up mutant.

I realise I am coming to this from a bit of a beginner's standpoint, which is perhaps not helping me to write this document in a universally applicable way. I'm happy to continue a discussion on this and to defer to experts!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reworked this a bit. As well as combining the mutation and mutation operator sections, I've reworked this section a bit to add in some of that motivation. Let me know what you think of it now!


* **Interface reduction**

TODO


## Mutation operator
[^1]: After all; these are not official terms handed down from an authority on mutation testing. We’ve invented them for the purposes of introducing the concepts here.

A transformation applied to the AST of a subject. Mutant knows the following high level operator
classes:
* **Orthogonal Replacement** - for example `>` -> `<`

* Semantic Reduction
* Orthogonal Replacement
* [Noop](#neutral-noop-tests)
Unlike semantic reduction, where the result is a “simpler” version of the input, an orthogonal replacement transforms some code with a given function into code with a similar semantic complexity, but which does something different (usually opposite). This category is probably better understood by examples:
telyn marked this conversation as resolved.
Show resolved Hide resolved
* `true` -> `false`
* `#>` -> `#<`

These transformations are less common,
* **No-Op** - This type of operator does nothing. It is needed in order to ensure that mutant’s presence, and prior mutations’ side effects, do not cause the test suite to fail.

An exhaustive list can be found in the [mutant-meta](https://github.com/mbj/mutant/tree/master/meta)
An exhaustive list can be found in the
subdirectory of the source.

## Mutation

The result of applying a mutation operator to the AST of a subject. A mutation represents a
hypothesis that ideally gets falsified by the tests.
The result of applying a mutation operator to the AST of a subject. A mutation
represents a hypothesis that ideally gets falsified by the tests. Some example hypotheses:

```ruby
# before application of mutation operator
def equal_to_5?(number)
number == 5
end

# after application of `#==` -> `#eql?`
# hypothesis: the conversion semantics of #== are not necessary for this method
def equal_to_5?
number.eql?(5)
end

# after removal of `number == 5`
# hypothesis: that line of the method is dead code
def equal_to_5?; end

# after replacement of `number == 5` with `true`
# hypothesis: the tests only check the happy-path
def equal_to_5?(number)
true
end
```

## Insertion

Expand Down
2 changes: 1 addition & 1 deletion docs/reading-reports.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Mutation output is grouped by selection groups. Each group contains three sectio

**Format**:

```text
, ```text
telyn marked this conversation as resolved.
Show resolved Hide resolved
- [INTEGRATION]:0:[SPEC LOCATION]:[SPEC DESCRIPTION]
- [INTEGRATION]:1:[SPEC LOCATION]:[SPEC DESCRIPTION]
```
Expand Down
50 changes: 50 additions & 0 deletions meta/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Mutant’s Mutation Operators
===

This directory contains all the [Mutation Operators](../docs/nomenclature.md#mutation-operator)[^mutation-operator] that Mutant can apply.

The filenames mostly correspond to the names of code elements as used by the [parser](https://github.com/whitequark/parser) and [unparser](https://github.com/mbj/unparser) libraries. For the most part they are the same as keywords within Ruby, but here are some explanations of the less obvious ones:

* `str` and `sym` are string and symbol literals that do not contain dynamic elements (`"I'm a dstring #{i_am_the_dynamic_element}"`)
* `dst` and `dsym` are the equivalents for strings and symbols which do
* `regexp` is a regular expression without any options (`/like this/`)
* `regopt` is a regular expression with options (`/like this/i`)
* a few come in regular and `-asgn` variants - the latter are the assignment versions. E.g., `||=` is the assignment version (`or_asgn.rb`) of `||` (`or.rb`)
* `gvar`, `cvar`, `ivar` and `lvar` refer to global (`$VERBOSE`), class (`@@debug`), instance (`@amount`) and local (`response`) variables, respectively.
* `csend` is the conditional send operator (`&.`)
* `cbase` is a top-level constant (`::Kernel` or `::Errno::ENOENT`), whereas `const` is a "normal" constant (`Kernel` or `Errno::ENOENT`)
* `casgn` is an assignment to a constant (`VERSION = "0.1.0"`)
Comment on lines +8 to +16
Copy link
Sponsor Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This strikes me as very helpful to new contributors :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

, or Things I Had To Learn When I Was Adding Metaclass Support 😁


All the files in this directory use a fairly simple DSL. To explain it, let's run through (a slightly modified version of) `return.rb`:

```ruby
# Adds an example matching expressions like `return foo`, where foo
# can be anything. In parser's s-expression DSL, this would match
# `s(:return, s(...))`, but not the simpler `s(:return)`.
Mutant::Meta::Example.add :return do
# Each Example can only have one source, which provides a pattern to match
source 'return foo'

# multiple `mutation`s are provided. Each is simply a ruby
# source-code representation of the code after the change, using
# the same placeholders as the source
mutation 'foo' # replaces `return foo` with `foo`
mutation 'return nil' # replaces `return nil` with `nil`
mutation 'return self' # replaces `return foo` with `return self`
end

# This Example matches the simpler `s(:return)` case (or simply,
# `return` expressions)
Mutant::Meta::Example.add :return do
source 'return'

# singleton_mutations is a convenience method for the following:
#  mutation 'nil'
#  mutation 'self'
# This just helps ensure consistency throughout - a very large
# number of expressions can be usefully mutated to `nil` or `self`
singleton_mutations
end
```

[^mutation-operator]: Remember, Mutation Operator just means “a change that can be applied to the code”