Skip to content

Reuse Projected Code #330

@JasonBock

Description

@JasonBock

Describe the solution you'd like
Right now, if Rocks encounters a type that it needs to generate code for (e.g. a pointer), it puts all that code into a Projections static class within the expectations class. However, this means that if a pointer type (e.g. int*) is encountered more than once in two different mock types, that code is generated for each mock type, even though it is identical.

What I could do is create models for projected code. Meaning, for each type that needs projected code (like delegates for callback or return types or custom Argument types), it would create a separate model + code file that would be in its own namespace. So, for example, if int* was used in a mock type, Rocks currently does something like this:

namespace MockTests
{
  [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
  internal sealed class IHavePointersCreateExpectations
    : global::Rocks.Expectations
  {
    internal static class Projections
    {
      internal unsafe delegate void Callback_675345066879799784342630291957923402524406707040(int* @value);
      internal unsafe delegate bool ArgumentEvaluationForintPointer(int* @value);
	  
      internal unsafe sealed class ArgumentForintPointer
        : global::Rocks.Argument
      {
        // ...argument implementation goes here....
      }
    }

    internal sealed class Handler0
      : global::Rocks.Handler<global::MockTests.IHavePointersCreateExpectations.Projections.Callback_675345066879799784342630291957923402524406707040>
    {
      public global::MockTests.IHavePointersCreateExpectations.Projections.ArgumentForintPointer @value { get; set; }
    }
    private global::Rocks.Handlers<global::MockTests.IHavePointersCreateExpectations.Handler0>? @handlers0;
      
    // Other Rocks code-gen goes here...
  }
}

What it would do instead is generate this separately:

namespace System
{
  [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
  internal partial static class Projections
  {
    internal unsafe delegate void Callback_675345066879799784342630291957923402524406707040(int* @value);
    internal unsafe delegate bool ArgumentEvaluationForintPointer(int* @value);
	
    internal unsafe sealed class ArgumentForintPointer
      : global::Rocks.Argument
    {
      // ...argument implementation goes here....
    }
}

Then, the generated code would reference these types:

internal sealed class Handler0
  : global::Rocks.Handler<global::System.Projections.Callback_675345066879799784342630291957923402524406707040>
    {
      public global::System.Projections.ArgumentForintPointer @value { get; set; }
    }
    private global::Rocks.Handlers<global::MockTests.IHavePointersCreateExpectations.Handler0>? @handlers0;

Since these would also be immutable models, they would only be generated once.

There would need to be further design work to determine if the generated Projections class should be partial (I'm going with that for now as there may be other types within that namespace that needs projections), along with figuring out how to create models with these types such that they're handled correctly by the compilation pipeline for caching purposes. Also, the namespace naming needs to be validated in terms of what is the "best" approach.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions