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

Develop a test to ensure game constants are covered #152

Open
pyrodogg opened this issue Dec 10, 2019 · 2 comments
Open

Develop a test to ensure game constants are covered #152

pyrodogg opened this issue Dec 10, 2019 · 2 comments

Comments

@pyrodogg
Copy link
Member

Game constants are defined here.

It should be possible to parse this file and verify that the constants (and types) are also defined in typed-screeps.

This would help significantly in catching missing constants.

@tcdejong
Copy link
Contributor

I haven't written such a test before but I guess it's relatively simple to do.
It could be something like: (psuedo code)

Load official constants into object A
Load typed-screeps constants into object B
Both objects should follow { [key: constant name]: constant value }
Importing/requiring the constants should automatically achieve this. 

Test 1 - Comparison of number of keys in A and B: 
- If numKeys A < numKeys B, superfluous constants should be removed.
- Create two sets of key names and output the difference: these should be removed.

Test 2 - Iterate over the entries (key, val pairs) of A:
For every entry check if `key` exists in `B`.
If so, check if the values match. 

Test 1 makes sure there's nothing left over in B that should be removed.
Test 2 makes sure that all keys in A are present in B with the correct value.

All that remains is formatting the output neatly.

@jsejcksn
Copy link

Hello. I'm just beginning to explore Screeps for the first time and plan to do so using Deno. In my beginning research, I found this repo and decided to take a look at the open issues. This one caught my eye.

While @tcdejong's idea seems straightforward, I think there is a major hurdle in the approach:

Load typed-screeps constants into object B

It doesn't seem that this repo actually provides any data values: only types (including literal types). I'm not aware of any project which will take a type declaration file of literal types and transform it into corresponding valid JavaScript data. If there is such a project, I'd love to know about it!

I haven't closely examined the files from the projects yet, but I had another idea that might help reduce manual effort in the meantime until a completely automated solution is discovered:

  • Fetch the source constants file
  • Fetch the distribution declaration file from this project
  • Evaluate the constants file, loading exports into an object
  • Concatenate the data into a new TypeScript file like so:
    • Append the declaration file
    • Append each key/value pair of the constants object as a new namespaced const variable assignment statement (using JSON.stringify for the values)
    • Append each key of the constants object as a new namespaced const variable (asserting it as true) and assigning it to a placeholder value (asserting the value as the result of a conditional type which compares the names from both files)

Here is a deno script that will do what I've described above, printing the result to stdout:

compare.ts:
async function fetchGitHubFile (
  owner: string,
  repo: string,
  branch: string,
  path: string,
): Promise<string> {
  const url = new URL(`https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path}`);
  await Deno.permissions.request({name: 'net', host: url.host});
  const response = await fetch(url.href);
  if (!response.ok) throw new Error('Response not OK');
  return response.text();
}

export async function main (): Promise<void> {
  const sourceConstantsFile = await fetchGitHubFile('screeps', 'common', 'master', 'lib/constants.js');
  const constants = eval(`
  (() => {
    const exports = {};
    ${sourceConstantsFile}
    return exports;
  })();
  `);

  // disable linting opinionated rules
  let text = `// deno-lint-ignore-file camelcase, no-explicit-any, no-empty-interface, no-unused-vars`;
  
  text += `\n\n// -------------------- Begin types file --------------------\n\n`;
  text += await fetchGitHubFile('screepers', 'typed-screeps', 'master', 'dist/index.d.ts');
  text += `\n\n// -------------------- End types file --------------------\n\n`;

  text += `\n\n// -------------------- Begin source constants file --------------------\n\n`;
  for (const [key, value] of Object.entries(constants)) {
    text += `const SOURCE_${key} = ${JSON.stringify(value)}${value === null ? '' : ' as const'};\n`;
  }
  text += `\n\n// -------------------- End source constants file --------------------\n\n`;
  
  text += `\n\n// -------------------- Begin tests --------------------\n\n`;
  for (const key of Object.keys(constants)) {
    text += `const TEST_${key}: true = 0 as unknown as (typeof SOURCE_${key} extends typeof ${key} ? true : false);\n`;
  }
  text += `\n\n// -------------------- End tests --------------------\n\n`;

  console.log(text);
}

if (import.meta.main) main();

Run in deno like this, redirecting to test-types.ts:

$ deno run compare.ts > test-types.ts
⚠️  ️Deno requests network access to "raw.githubusercontent.com". Grant? [g/d (g = grant, d = deny)] g

Finally, use TypeScript to reveal the errors. Here is a screenshot of the problems panel in VS Code:

image

All of the errors which say "Type 'false' is not assignable to type 'true'" are from the conditional types in the last step.

This can certainly be improved, and is not an automated or exhaustive testing strategy, but I think it can provide some help in debugging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants