<commit_before>﻿#Shience
A .NET port(ish) of Github's Scientist library. (https://github.com/github/scientist)

##How do I do science?
Let's pretend you're doing the same thing as in Scientist's example and are changing the way you're handling permissions. Unit tests help, but it's useful to compare behaviors under load, in real conditions. Shience helps with that.

```csharp
//Set a publisher
Shience.SetPublisher(new FilePublisher(@"C:\file\path\to\results.txt"));
    
var science = Shience.New<bool>("widget-permissions");
    
var userCanRead = science.Test(
    control: (() => return UserPermissions.CheckUser(currentUser); ), 
    candidate: (() => return User.Can(currentUser, Permission.Read); )
);

if(userCanRead)
{
    //do things!
}
```
                             
Shience will run the control (the old way) and the candidate (the new way) in random order. It will return the control result to you for use, but will also compare the control result with the candidate result to determine whether the behaviors are the same. It will publish the comparison result using the publisher specified.

##Context
Test results sometimes aren't useful without context. You can add objects that you might feel are useful when viewing comparison results. The context objects will be published with the rest of the data.

```csharp
var userCanRead = science.Test(
    control: (() => return UserPermissions.CheckUser(currentUser); ), 
    candidate: (() => return User.Can(currentUser, Permission.Read); ),
    contexts: new[] {currentUser, "Within DisplayWidget method", DateTime.UtcNow }
);
```
                                
##Comparing
Objects can be hard to compare. You can specify how to compare them in 2 ways.

###Override `Equals`
Shience, by default, compares results using `.Equals`. You can override `Equals` and `GetHashCode` on your object and compare that way.

```csharp
private class TestHelper
{
    public int Number { get; set; }

    public override bool Equals(object obj)
    {
        var otherTestHelper = obj as TestHelper;
        if (otherTestHelper == null)
        {
            return false;
        }

        if (otherTestHelper.Number == this.Number)
        {
            return true;
        }

        return false;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode() ^ Number;
    }
}
```

then

```csharp
var result = science.Test(
                        control: (() => { return new TestHelper {Number = 1}; }),
                        candidate: (() => { return new TestHelper {Number = 2}; })
                    );
```

###Pass in a custom `Func<>`
You can also pass in a comparing `Func<>` to the `Test` method.

```csharp
var userCanRead = science.Test(
                              control: (() => return UserPermissions.CheckUser(currentUser); ), 
                              candidate: (() => return User.Can(currentUser, Permission.Read); ),
                              comparer: (controlResult, candidateResult) => { return controlResult == candidateResult; }
                         );
```

##Async

Tests can be run in parallel using the `TestAsync` method. When run in parallel the order in which they start is no longer randomized. To run tests in parallel, `await` the `TestAsync` method:

```csharp
var result = await science.TestAsync(
                                  control: (() => { Thread.Sleep(5000); return true; }),
                                  candidate: (() => { Thread.Sleep(5000); return true; }),
                              );
```<commit_msg>Added disclaimer about write operations<commit_after>﻿#Shience
A .NET port(ish) of Github's Scientist library. (https://github.com/github/scientist)

##How do I do science?
Let's pretend you're doing the same thing as in Scientist's example and are changing the way you're handling permissions. Unit tests help, but it's useful to compare behaviors under load, in real conditions. Shience helps with that.

```csharp
//Set a publisher
Shience.SetPublisher(new FilePublisher(@"C:\file\path\to\results.txt"));
    
var science = Shience.New<bool>("widget-permissions");
    
var userCanRead = science.Test(
    control: (() => return UserPermissions.CheckUser(currentUser); ), 
    candidate: (() => return User.Can(currentUser, Permission.Read); )
);

if(userCanRead)
{
    //do things!
}
```
                             
Shience will run the control (the old way) and the candidate (the new way) in random order. It will return the control result to you for use, but will also compare the control result with the candidate result to determine whether the behaviors are the same. It will publish the comparison result using the publisher specified.

##Context
Test results sometimes aren't useful without context. You can add objects that you might feel are useful when viewing comparison results. The context objects will be published with the rest of the data.

```csharp
var userCanRead = science.Test(
    control: (() => return UserPermissions.CheckUser(currentUser); ), 
    candidate: (() => return User.Can(currentUser, Permission.Read); ),
    contexts: new[] {currentUser, "Within DisplayWidget method", DateTime.UtcNow }
);
```
                                
##Comparing
Objects can be hard to compare. You can specify how to compare them in 2 ways.

###Override `Equals`
Shience, by default, compares results using `.Equals`. You can override `Equals` and `GetHashCode` on your object and compare that way.

```csharp
private class TestHelper
{
    public int Number { get; set; }

    public override bool Equals(object obj)
    {
        var otherTestHelper = obj as TestHelper;
        if (otherTestHelper == null)
        {
            return false;
        }

        if (otherTestHelper.Number == this.Number)
        {
            return true;
        }

        return false;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode() ^ Number;
    }
}
```

then

```csharp
var result = science.Test(
                        control: (() => { return new TestHelper {Number = 1}; }),
                        candidate: (() => { return new TestHelper {Number = 2}; })
                    );
```

###Pass in a custom `Func<>`
You can also pass in a comparing `Func<>` to the `Test` method.

```csharp
var userCanRead = science.Test(
                              control: (() => return UserPermissions.CheckUser(currentUser); ), 
                              candidate: (() => return User.Can(currentUser, Permission.Read); ),
                              comparer: (controlResult, candidateResult) => { return controlResult == candidateResult; }
                         );
```

##Async

Tests can be run in parallel using the `TestAsync` method. When run in parallel the order in which they start is no longer randomized. To run tests in parallel, `await` the `TestAsync` method:

```csharp
var result = await science.TestAsync(
                                  control: (() => { Thread.Sleep(5000); return true; }),
                                  candidate: (() => { Thread.Sleep(5000); return true; }),
                              );
```

##Designing an experiment

Due to the fact that both the control *and* candidate have the possibility of running, Shience **should not** be used to test **write** operations. If Shience is set up to run a write operation, it's entirely possible that the write could happen twice (which is probably not wanted). 

It's best to only do science on read operations. 
{'fraction_non_alphanumeric': 0.1035078394897688, 'fraction_numerical': 0.0029231995748073346, 'mean_word_length': 2.5962732919254656, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 31, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '438932', 'n_tokens_mistral': 2004, 'n_tokens_neox': 1903, 'n_words': 776}