GithubHelp home page GithubHelp logo

jsl-hamcrest's Introduction

JSL-Hamcrest

Implementation of Hamcrest (hamcrest.org) for JSL. Write expressive tests with informative failure messages. Make tests more self-contained and reduce information spilling between them.

Abstract - JMP Discovery Summit 2019

Automate the Testing of JSL Using Hamcrest

Have you written some JSL and gotten tired of manually testing after every change? Have you inadvertently broken some piece of your application, or has the fear of doing so prevented you from making the changes you want to make? With automated testing, you can be more confident in your changes. Now available is a set of tools for automating the testing of JSL. This framework includes the creation of tests and test cases, as well as an implementation of the well-known Hamcrest assertion library. Hamcrest provides flexible ways to assert what you know to be true about the behavior of your JSL. These tools are full-featured and can be specialized for your needs. In fact, JMP development even uses them to test JMP itself. The presentation will cover this framework and its use in testing an example JSL application, from individual functions to the automation of GUI interactions.

Documentation

Check out our documentation website for detailed information about using JSL-Hamcrest.

Getting Started

System Requirements

  • Works on Mac and Windows operating systems
  • Requires JMP 14.1 or higher

Installing

Open the JSL-Hamcrest.jmpaddin file in JMP to install.

Basic Usage

JSL-Hamcrest has several different pieces that you can choose to use for testing. You can use most of these separately or you can combine them. These include assertions (ut assert that and matchers), test cases (ut test, ut test case), mock functions (ut mock function), and the reporter (ut global reporter) that underlies all of this. The most common scenario is a combination of test cases and assertions like:

formulas test case = ut test case("Formulas")
    << Setup(Expr(
        dt = Open("$SAMPLE_DATA\Big Class.jmp");
    ))
    << Teardown(Expr(
        Close(dt, NoSave); // Unnecessary
    ))
    << Skip If(Expr(Day Of Week(Today()) == 4), "It's Wednesday");

ut test(formulas test case, "Advance Row", Expr(
    dt << new column("Test", Formula(Row()));
    ut assert that(Expr(dt:Test << Get Values), (1::40)`);
));

ut test(formulas test case, "Char", Expr(
    dt << new column("Test", Formula(Char(Row())));
    ut assert that(Expr(dt:Test << Get Values), ut starts with({"1", "2", "3"}));
));

ut test("Big Class", "Row Count", Expr(
	dt = Open("$SAMPLE_DATA\Big Class.jmp");
	ut assert that(Expr(N Rows(dt)), ut greater than(38), "large");
));

The ut test case allows a Setup and Teardown expression that are run before and after each ut test. The ut test also ensures that all windows, tables, errors, and symbols get cleaned up so you can focus on the test itself. The ut assert that is how you write an actual assertion. The first argument (the test expression) should always be wrapped in Expr so we can test errors and report better test failures. The second argument (the expected) can be a value or a matcher like ut starts with. These are declarative descriptions of what you would like to test which give informative descriptions and failures. You can use ut assert that outside of ut test if you want.

Mock functions can be used to test callbacks. These look like:

Data Table List Subscription Test = ut test case("Data Table List Subscription")
    <<Setup(Expr(
        mock open = ut mock function({dt}, Expr("open"; .));
        sub name = Subscribe to Data Table List(, OnOpen( Function({dt}, mock open:fn(dt)) ) );
    ))
    <<Teardown(Expr(
        Unsubscribe to Data Table List(sub name, "ALL");
        ut verify mock(mock open);
    ));

ut test(Data Table List Subscription Test, "OnOpen/OnClose called for Open", Expr(
    ut expect call(mock open, { ut name( ut host table name( "Big Class" ) ) } );
    Open( "$SAMPLE_DATA/Big Class.jmp" );
));

ut mock function defines a instrumented function that you can use as a callback. ut expect call adds expectations about how many times that function will be called and with what arguments. And ut verify mock makes sure the expectations are met.

Finally, there are reporters. Most of the time, you don't need to worry about the reporter since it is handled automatically by the add-in when initialized or when a test runner GUI is added to a script editor. However, if you want to do something else, you need to know how to use a reporter. There can only be one reporter, ut global reporter, and it determines what happens when test successes, failures, etc are reported from ut mock function, ut assert that, ut test, etc. Here are examples showing reporters and how to use them.

// Does nothing
ut global reporter = ut reporter();
ut assert that(Expr(1 + 1), 3);
// Writes to the log as successes, failures, etc come in
ut global reporter = ut streaming log reporter with skip();
ut assert that(Expr(1 + 1), 3);
// Collects into a buffer until you show the report explicitly
ut global reporter = ut collecting reporter();
ut assert that(Expr(1 + 1), 3);
ut global reporter << show report();
// Saves test results as rows in a JMP data table
ut global reporter = ut data table reporter();
ut test("Addition", "Addition works", Expr( ut assert that(Expr(1 + 1), 3, "woops") ) );
ut global reporter << New Data View;

Again, most of the time, you don't need to worry about setting ut global reporter explicitly as the add-in handles it for you.

Using the Hamcrest Test-Runner Add-In

  1. Install the Add-In
  2. Write tests in a JMP script editor
  3. Attach a Test-Runner to the editor
  4. Press F5

Writing a New Matcher

The ability to create new matchers is part of what makes JSL-Hamcrest so powerful. You can create matchers for your own specific use case, or contribute one back to the community.

UtMatcher Class

The first step is to define a class that has UtMatcher as its base class. You just need to define the _init_ (if needed), matches, and describe methods within your class.

/*  Class: UtDayOfWeekMatcher
        Checks the day of week for a date.
*/
Define Class( "UtDayOfWeekMatcher",
    Base Class( "UtMatcher" ),
    value = .;
    days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
    // Method: _init_
    // Needed if value or inner matcher needs to be saved
    _init_ = Method( {value},
        this:value = value;
    );
    // Method: matches
    // Return match info with success/failure info
    matches = Method( {test expr},
        // This evaluates the test expression
        actual = test expr;
        // Dates are numbers in JMP, so check the type first
        // Name Expr() is needed to prevent actual from
        // evaluating again in case it is an expression.
        If( !Is Number( Name Expr( actual ) ),
                // This returns the match info
                ut match info failure(
                    Eval Insert( "^Name Expr( actual )^ was of type ^Type( Name Expr( actual ) )^" )
                ),
            // If matches the correct day of week, return success
            Day of Week( actual ) == this:value,
                ut match info success(),
            // else not on given day of week, return failure
                ut match info failure(
                    Eval Insert( "^As Date( actual )^ was on ^this:days[Day of Week( actual )]^" )
                )
        );
    );

    // Method: describe
    // Describes the be expected outcome
    describe = Method( {},
        Eval Insert( "date falling on a ^this:days[Day of Week( this:value )]^" )
    );
);

Matcher Factory

After you have the matcher defined, you need to create a factory function. This function's name is what will be used in assertions. Include a call to ut matcher factory to register the matcher needed for some other matchers.

/*  Function: ut day of week
        Factory function for <UtDayOfWeekMatcher>.

    Example:
        ---JSL---
        d = 01aug2019;
        ut assert that( Expr( d ), ut day of week( 5 ) );
        ---------
*/
ut matcher factory(
  "ut day of week",
  Expr(Function( {val},
    // Do necessary type checking of input variables
    // in factory function.
    If( !Is Number( val ),
      Throw( "ut day of week() requires a numeric date value as the argument" ),
    );
    New Object( UtDayOfWeekMatcher( Name Expr( val ) ) );
  )),
  "ut day of week( value )", //Prototype
  "Matches based on the day of the week of a given date value." // Description
);

UtTypedMatcher

You can also derive from UtTypedMatcher to have any type checking done for you. This simplifies a lot of what happens in the matches method.

Define Class( "UtDayOfWeekMatcher",
    Base Class( "UtTypedMatcher" ),
    value = .;
    days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
    allowable types = {Date, Number};
    _init_ = Method( {value},
        this:value = value;
    );
    // Method: typed matches
    // Handle a pre-evaluated and typed checked actual value.
    typed matches = Method( {actual},
        day = Day of Week( actual );
        If( day == this:value,
                ut match info success(),
            // else
                ut match info failure(
                    Eval Insert( "^As Date( actual )^ was on ^this:days[Day of Week( actual )]^" )
                )
        );
    );

    describe = Method( {},
        Eval Insert( "date falling on a ^this:days[Day of Week( this:value )]^" )
    );
);

With deriving from UtTypedMatcher, you are required to inject a self reference onto the matcher within the factory function.

ut day of week = Function( {val},
    If( !Is Number( val ),
        Throw( "ut day of week() requires a numeric date value as the argument" ),
    );
    ut new object( "UtDayOfWeekMatcher", Eval List( {Name Expr( val )} ) );
);

Multiple Factories

You can have multiple factory functions for a single matcher to either change the comparison method (like less than/greater than) or to improve the wording, such as ut on thursday() instead of ut day of week( 5 ).

ut on thursday = Function( {},
    ut day of week( 5 );
);

Building Documentation

  1. Download Natural Docs
  2. Navigate to the root of this project
  3. Run NaturalDocs

    NaturalDocs Docs

  4. Open Docs/_html/index.html

Releasing a Version

See Semantic Versioning for details on version numbers.

  1. Check CHANGELOG.md, addin.def, and Source/DevAddin/addin.def for correct (current) version number.
  2. Build the documentation and remove Working Data folder.
  3. Zip up the entire repository with addin.def at the root (don't include the .git or .github folder).
  4. Rename the zip file as JSL-Hamcrest-v<MAJOR>.<MINOR>.<PATCH>.jmpaddin.
  5. Zip up the Source/DevAddin folder with its addin.def at the root.
  6. Rename the zip file as JSL-Hamcrest-Dev-v<MAJOR>.<MINOR>.<PATCH>.jmpaddin.
  7. Push a tag of the form v<MAJOR>.<MINOR>.<PATCH>.
  8. Create a GitHub release. Add the relevant CHANGELOG section there and attach the two addins.
  9. Update the gh-pages branch
  10. Create a new Pull Request bumping the version number to the next predicted (see files from (1))

Support

We use GitHub for tracking bugs and feature requests. Please submit a GitHub issue or pull request for support.

Contributing

We welcome your contributions! Please read CONTRIBUTING.md for details on how to submit contributions to this project.

License

This project is licensed under the Apache 2.0 License.

Attributions

Icons used in the JSL-Hamcrest add-in were adapted from the Material Design Icons by Google under their Apache 2.0 License.

Additional Resources

  • Original presentation materials from JMP Discovery Summit Europe 2019 can be found here.
  • Hamcrest.org

jsl-hamcrest's People

Contributors

dougiechong avatar emccorkle avatar himanga avatar jefurbee avatar justinchilton avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsl-hamcrest's Issues

Create test suite to test JSL-Hamcrest

To provide test coverage, we will add some tests for our testing library itself. We will start with a core set of tests and build them out over time, including any new changes requiring tests added.

We can test some of the jsl-hamcrest library using hamcrest itself, but the core will be tested using other means.

Initializing the Addin disconnects an already configured reporter

  1. Configure a ut global reporter
  2. Initialize the JSL-Hamcrest Addin
  3. Notice that your ut global reporter is now a UtWindowDispatchingReporter with a fallback UtStreamingLogReporter.

It should instead be a UtWindowDispatchingReporter with your configured reporter from step (1). This way, tests run via a Window's Test Runner GUI will use that GUI via UtWindowDispatchingReporter. But tests run otherwise will go to the reporter from step (1) instead of just to the log. If step (1) is omitted, we should still use a UtStreamingLogReporter.

ut test should save and restore current row number to better isolate tests

When ut test finishes, we currently close windows and delete symbols that were created during the test. This is done in an attempt to isolate the tests from each other and to avoid lots of boilerplate teardown expressions. However, there are more things we can do to improve this isolation.

If the test changes the current row (via Row() or otherwise), it should get reset to its pre-test value. Otherwise, test order could affect the results of the test. I'm filing this as a bug since it is in the spirit of the existing reset capability but had previously been overlooked.

Include log failures in ut test as failures in return code

Log failures are being reported as failures to the global reporter, but are by-passing the mechanism to trap assertion errors within ut test. This makes it possible for a 1 to be returned by ut test when the only failure in a test is a log failure. It should use the local assertion function to detect log failures.

Approx matrix showing expected value in actual mismatch

To reproduce:

ut assert value([5], ut approx([6]));

Output:

FAILURE  Test #2836: Expr([5]) approximately [6] where zero e ... () 
         Actual: 6 was not within relative epsilon at position [1, 1] within [6]
       Expected: approximately [6] where zero epsilon=1e-12, relative epsilon=1e-9
            LRE: [0.778151250383644]

Create function make new objects with self references

Something like this should do:

ut new object = Function( {class, args},
    {obj},
    obj = New Object( class, args );
    obj:self = obj;
);

This would simplify the new object creation of objects with injected self references.

ut my matcher = Function( {matcher},
    ut new object( "MyMatcher", Eval List( {ut ensure matcher( Name Expr( matcher ) )} ) ); 
);

Nested tests can't independently use log benchmarking

ut test("Outer", "Test Captures", Expr(
	ut test("Inner", "Test Captures Too", Expr(
		Print("stuff")
	), ut log bench(0));
), ut log bench(0));

The outer and inner tests both use log benchmarking. This means they explicitly (through ut log bench()) or implicitly (through ut log bench default) use 0 or 1 for the log benchmarking option. When this happens the global temporary reporters in TestCase.jsl#L282-L285 get clobbered and the ut global reporter is broken. If either one used -1 instead, there is no problem.

We should be using locally constructed instances for those two temporary reporters instead of globals.

Split label to avoid downstream parsing by reporters

Currently, the ut test isn't quite a first class citizen due to how its information is given to Reporters. The test case name, test name, assertion label, and assertion number are all combined into a single label. See TestCase.jsl#L581-L585 and Utils.jsl#L472-L481. This was done for compatibility reasons but now complicates more than it helps. Reporters that wish to be "ut test aware" must currently look for ut concat test label sep and parse out these separate pieces (see Reporters.jsl#L279-L290). Instead, we should formally split the label into several pieces and pass them directly to the Reporter.

This should go in 2.0.0 as it is a breaking change that affects the Reporter interface.

Empty matrix fails UtEqualToMatcher

case = uttestcase("Test");
uttest(case, "empty matrix", 
	Expr(
		x = [];
		utassertthat(Expr(x), [])
	)
);

returns

FAILURE  x equal to [] (Test ⮚ empty matrix ⮚ 1)
    Actual: threw "Error: invalid subscript (must be number or list of numbers). 1 time. actual[ /*###*/Loc(!actual missing)]"
    Expected: equal to []

Require all reporter classes to have associated factory functions

To make it easier to create reporter objects (e.g. ut reporter() vs New Object( UtReporter() )), we will now require that reporter classes have associated factory functions.

NOTE: This change does have a compatibility issue, in that if you already have references to something like New Object( UtCollectingReporter() ), it will no longer work. You will need to either add quotes to the call New Object( "UtCollectingReporter"() ) or use the new factory function ut collecting reporter().

Embedded log tab is difficult to use

The embedded log can look incorrect when initially starting the test runner if the embedded log is not on. Solution will be to not show the embedded log by default and always show it's tab within the runner.

Data Table Matcher

I'd suggest a data table matcher. Currently for our implementation we're using this

/* 
	Class: UtEqualToDataTableMatcher
		---Prototype---
		class UtEqualToDataTableMatcher inherits UtEqualToMatcher
		---------------
		
		Operates only on datatables. See <UtEqualToMatcher>.
*/
Define Class(
	"UtEqualToDataTableMatcher",
	Base Class( UtEqualToMatcher ),
	_init_ = Method({}, super:matches value = Name Expr( this:matches value ) );
	matches value = Method({actual},
		obj = actual << Compare Data Tables( compare With( this:value ) );
		
		If( !(obj << Are Data Different),
			::ut match info success();
		,
			mismatch = "was " || Char( actual );
			LRE = ::ut global lre( actual, this:value );
			::ut match info failure( mismatch, LRE );
		);
	);
);

but this doesn't take into account table variables, or scripts. It just checks that the data are the same.

Convert docs to Sphinx

To improve the look/feel and usability of the docs, it would be nice to switch from Natural Docs to Sphinx.

Reporter improvements

I'd like to see timing and module in the reporters as well.

Timing

Timing would allow us to see if performance changes across different runs.
I think having an HPTime() on start and end of uttestcase() and the utassertthat() would help us see performance differences. I'm probably missing some places.

Module

Also I was thinking about adding on each of the reporter add X() functions. For instance,

// Function: add failure
add failure = Method( {label, test expr, description, mismatch, lre, payload=Empty()},
	file_name = Include File List()[1];
	record = Eval List( {label, Name Expr( test expr ), description, mismatch, lre,  Name Expr( payload ), file_name} );
	Insert Into( this:failures, Eval List( {record} ) );
	0;
);

I'm not sure what impact that would have elsewhere though.

Add pull request template for adding new matchers

Checklist should include (at least) the following.

  • Included documentation with at least one example and a description.
  • Added tests in Tests/UnitTests/Matchers.
  • Added matcher to Source/Matchers-Index.txt.
  • Added note to CHANGELOG.md.

UtExpressionCompositeMatcher doesn't support strings with children

The UtExpressionCompositeMatcher produced by ut expression matches does not handle strings with children well. I believe that it simply ignores the children and compares the strings.

This comes up when testing the structure of a JSL exception_msg. Something like "some message"(1, 2)

JUnit Reporter

I'd like to have a reporter for JUnit. We've already created it. Would you recommend putting it in reporters or the new custom folder?

Trivial assertions result in "Expression Failure" message

ut test( "Simple Pass", "One equals one", Expr(
	ut assert that( Expr(1), ut equal to(1) );
));

ut test( "Simple Pass", "dog equals dog", Expr(
	ut assert that( Expr("dog"), ut equal to("dog") );
));

The trivial test cases shown above result in an "Expression Failure" message

Variable collision on functions with non-local variables.

Issue

Hamcrest fails when functions have non-local variables it seems

replicable code

f = function({}, 
    {dt//, h1 // it seems that any variable not local will error out, 
    },
    dt = open("$SAMPLE_DATA\Big Class.jmp");
    h1 = dt:height[1];
    return(h1);
);
case = uttestcase("Testy");
uttest(case, "dt scoping", 
    	utassertthat(Expr(f()), 59)
);

failure

FAILURE f() equal to 59 Actual: threw "Error: The member variable "h1" defined in class "UtEqualToNumberMatcher" cannot be accessed from the function "f". 1 time." Expected: equal to 59

Need a development addin that dynamically updates

Developing new Matchers and Reporters can be difficult because they are not included in your installed version of the JSL-Hamcrest addin. This means you have to avoid using the installed addin or have to use it and then additionally include your new or modified files.

Instead, we should have a development version of the addin which can be directed to a repository on disk. This addin will have all the same actions as the normal released addin, but it will simply include the code from the configured directory.

This means, as you switch branches and update code, the dev addin can use those updates by simply restarting the test runner gui or reinitializing.

Do not overwrite ut global reporter

If a global reporter is already defined we should not overwrite it because we do not want to clobber anything already set by the user. This would allow for running files that re-import the library without overwriting the reporter.

Clarify failure message when empty expression is evaluated with ut assert that().

In all four of the tests below an empty value is evaluated, but in the first three tests a failure message seems to indicate that the test code was written incorrectly, rather than the test failing because the function being tested didn't do what it was supposed to.

ut with reporter( ut streaming log reporter(), Expr( 
	ut test( "Example", "Unclear Result", Expr(
		ut assert that( .                    , 1 );
		ut assert that( Expr( . )            , 1 );
		ut assert that( if( 1==2, 1 )        , 1 );
		ut assert that( Expr( if( 1==2, 1 ) ), 1 );
	))
));

the resulting log messages are:

FAILURE  Test #1: test expr first argument to assertion is ... (Example ⮚ Unclear Result ⮚ 1) 
         Actual: was not an expression
       Expected: first argument to assertion is an expression
FAILURE  Test #2: test expr first argument to assertion is ... (Example ⮚ Unclear Result ⮚ 2) 
         Actual: was not an expression
       Expected: first argument to assertion is an expression
FAILURE  Test #3: test expr first argument to assertion is ... (Example ⮚ Unclear Result ⮚ 3) 
         Actual: was not an expression
       Expected: first argument to assertion is an expression
FAILURE  Test #4: If(1 == 2, 1) equal to 1 (Example ⮚ Unclear Result ⮚ 4) 
         Actual: was .
       Expected: equal to 1
            LRE: .

The last message is useful, I propose this should be the behavior anytime the first argument of ut assert that, and maybe other functions, evaluates as missing.

ut optional within UtExpressionCompositeMatcher not working as expected

I would expect the ut optional token to work even if there are not the same number of arguments (because that arg would be optional). Below is an example that I would expect to pass.

Names Default To Here( 0 );
ut global reporter = ut streaming log reporter();
dt = Open( "$SAMPLE_DATA\Big Class.jmp" );
gb = dt << Run Script( "Graph Builder Smoother Line" );
ut assert that(
	Expr( gb << get script ),
	ut expression matches(
		Expr(
			Graph Builder(
				ut optional( size( ut wild(), ut wild() ) ),
				Show Control Panel( 0 ),
				Variables( X( :height ), Y( :weight ), Overlay( :sex ) ),
				Elements( Points( X, Y, Legend( 1 ) ), Smoother( X, Y, Legend( 2 ) ) )
			)
		)
	)
);

I wrote this matcher a long time ago, so I am not sure what the behavior was meant to be, but I think it is supposed to support this.

Test Expr can be reported differently with log benchmarking

The test expr is reported differently for ut assert value when log benchmarking is enabled. This is related to UtBufferingReporter used in the implementation of log benchmarking.

ut test("A", "a", Expr( ut assert value(10, 20) ), ut log bench(-1));
FAILURE  Test #1: Expr(10) equal to 20 (A ⮚ a ⮚ 1) 
                  ^ === HERE ===
            
ut test("A", "b", Expr( ut assert value(10, 20) ), ut log bench(0));
FAILURE  Test #2: 10 equal to 20 (A ⮚ b ⮚ 1) 
                  ^ === VERSUS HERE ===

Matrix matcher broken for empty matrices

The error shown is an error in the matcher code.

To reproduce:

ut assert that( Expr([] || []), []);

Error:

FAILURE  Cumulative Sum([]) equal to [] 
    Actual: threw "Error: invalid subscript (must be number or list of numbers). 1 time. Data Table Indices: {2} actual[ /*###*/Loc(!actual missing)]"
    Expected: equal to []

Allow unregistering of matchers

Matchers are registered using ut matcher factory, but you may want to unregister a matcher factory for a matcher only defined in a private test file. Adding a ut unregister mater factory function will allow cleanup after being finished with a matcher.

Add instance of matcher

An instance of matcher could be used to assert that something is a class object with a particular name. This would use the << Get Name message for classes, and therefore will not work for base classes

Remove risk of name collision in ut assert that

It is possible to have name collisions with ut assert that local variables, such as test expr, label, matcher, etc. Moving these locals to an anonymous namespace will help reduce the risk of name collisions.

Conditionally add functions to Scripting Index using custom functions

We will need to investigate the best way to do this, because not everyone would necessarily want the functions installed. And since the functions are in globals, is there a good way for us to organize them together in the Scripting Index? And can we do this without crowding up the source code while still making it manageable to make sure each function has a definition.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.