Skip to content

Commit c12c282

Browse files
authored
Merge pull request #30 from Unity-Technologies/UNI-21325-test-selection-behavior
Uni 21325 test selection behavior
2 parents 8815d2c + dffa561 commit c12c282

File tree

1 file changed

+286
-0
lines changed

1 file changed

+286
-0
lines changed
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
using UnityEngine;
2+
using UnityEditor;
3+
using UnityEngine.TestTools;
4+
using NUnit.Framework;
5+
using System.IO;
6+
using System.Collections.Generic;
7+
using FbxSdk;
8+
9+
namespace FbxExporters.UnitTests
10+
{
11+
/// <summary>
12+
/// Tests the default selection export behavior.
13+
/// Tests that the right GameObjects are exported and
14+
/// that they have the expected transforms.
15+
/// </summary>
16+
public class DefaultSelectionTest
17+
{
18+
private string _filePath;
19+
protected string filePath { get { return string.IsNullOrEmpty(_filePath) ? Application.dataPath : _filePath; } set { _filePath = value; } }
20+
21+
private string _fileNamePrefix;
22+
protected string fileNamePrefix { get { return string.IsNullOrEmpty(_fileNamePrefix) ? "_safe_to_delete__" : _fileNamePrefix; }
23+
set { _fileNamePrefix = value; } }
24+
25+
private string _fileNameExt;
26+
protected string fileNameExt { get { return string.IsNullOrEmpty(_fileNameExt) ? ".fbx" : _fileNameExt; } set { _fileNameExt = value; } }
27+
28+
private string MakeFileName(string baseName = null, string prefixName = null, string extName = null)
29+
{
30+
if (baseName==null)
31+
baseName = Path.GetRandomFileName();
32+
33+
if (prefixName==null)
34+
prefixName = this.fileNamePrefix;
35+
36+
if (extName==null)
37+
extName = this.fileNameExt;
38+
39+
return prefixName + baseName + extName;
40+
}
41+
42+
protected string GetRandomFileNamePath(string pathName = null, string prefixName = null, string extName = null)
43+
{
44+
string temp;
45+
46+
if (pathName==null)
47+
pathName = this.filePath;
48+
49+
if (prefixName==null)
50+
prefixName = this.fileNamePrefix;
51+
52+
if (extName==null)
53+
extName = this.fileNameExt;
54+
55+
// repeat until you find a file that does not already exist
56+
do {
57+
temp = Path.Combine (pathName, MakeFileName(prefixName: prefixName, extName: extName));
58+
59+
} while(File.Exists (temp));
60+
61+
return temp;
62+
}
63+
64+
protected GameObject m_root;
65+
66+
[TearDown]
67+
public void Term ()
68+
{
69+
foreach (string file in Directory.GetFiles (this.filePath, MakeFileName("*"))) {
70+
File.Delete (file);
71+
}
72+
if (m_root) {
73+
UnityEngine.Object.DestroyImmediate (m_root);
74+
}
75+
}
76+
77+
[Test]
78+
public void TestDefaultSelection ()
79+
{
80+
// Default selection behavior:
81+
// - Export descendants
82+
// - Don't export siblings
83+
// - Don't export parents
84+
// - If both a parent and descendant are selected,
85+
// then result will be the same as if just the parent
86+
// were selected
87+
//
88+
// Default transform export:
89+
// - if there is only one root GameObject being exported
90+
// then zero out the root transform, leave all descendants
91+
// with local transform
92+
// - if there are multiple root GameObjects, then export
93+
// the global transform of each, and local transform
94+
// of descendants
95+
96+
m_root = CreateHierarchy ();
97+
Assert.IsNotNull (m_root);
98+
99+
// test Export Root
100+
// Expected result: everything gets exported
101+
// Expected transform: root is zeroed out, all other transforms unchanged
102+
var exportedRoot = ExportSelection (new Object[]{m_root});
103+
CompareHierarchies(m_root, exportedRoot, true, false);
104+
CompareGlobalTransform (exportedRoot.transform);
105+
106+
// test Export Parent1, Child1
107+
// Expected result: Parent1, Child1, Child2
108+
// Expected transform: Parent1 zeroed out, all other transforms unchanged
109+
var parent1 = m_root.transform.Find("Parent1");
110+
var child1 = parent1.Find ("Child1");
111+
exportedRoot = ExportSelection (new Object[]{parent1.gameObject, child1.gameObject});
112+
CompareHierarchies(parent1.gameObject, exportedRoot, true, false);
113+
CompareGlobalTransform (exportedRoot.transform);
114+
115+
// test Export Child2
116+
// Expected result: Child2
117+
// Expected transform: Child2 zeroed out
118+
var child2 = parent1.Find("Child2").gameObject;
119+
exportedRoot = ExportSelection (new Object[]{child2});
120+
CompareHierarchies(child2, exportedRoot, true, false);
121+
CompareGlobalTransform (exportedRoot.transform);
122+
123+
// test Export Child2, Parent2
124+
// Expected result: Parent2, Child3, Child2
125+
// Expected transform: Child2 and Parent2 maintain global transform
126+
var parent2 = m_root.transform.Find("Parent2");
127+
exportedRoot = ExportSelection (new Object[]{child2, parent2});
128+
129+
List<GameObject> children = new List<GameObject> ();
130+
foreach (Transform child in exportedRoot.transform) {
131+
children.Add (child.gameObject);
132+
}
133+
CompareHierarchies(new GameObject[]{child2, parent2.gameObject}, children.ToArray());
134+
}
135+
136+
/// <summary>
137+
/// Compares the global transform of expected
138+
/// to the local transform of actual.
139+
/// If expected is null, then compare to the identity matrix.
140+
/// </summary>
141+
/// <param name="actual">Actual.</param>
142+
/// <param name="expected">Expected.</param>
143+
private void CompareGlobalTransform(Transform actual, Transform expected=null){
144+
var actualMatrix = ConstructTRSMatrix (actual);
145+
var expectedMatrix = expected == null? new FbxAMatrix() : ConstructTRSMatrix (expected, false);
146+
Assert.AreEqual (expectedMatrix, actualMatrix);
147+
}
148+
149+
/// <summary>
150+
/// Constructs a TRS matrix (as an FbxAMatrix) from a tranform.
151+
/// </summary>
152+
/// <returns>The TRS matrix.</returns>
153+
/// <param name="t">Transform.</param>
154+
/// <param name="local">If set to <c>true</c> use local transform.</param>
155+
private FbxAMatrix ConstructTRSMatrix(Transform t, bool local=true)
156+
{
157+
var translation = local? t.localPosition : t.position;
158+
var rotation = local? t.localEulerAngles : t.eulerAngles;
159+
var scale = local? t.localScale : t.lossyScale;
160+
return new FbxAMatrix (
161+
new FbxVector4 (translation.x, translation.y, translation.z),
162+
new FbxVector4 (rotation.x, rotation.y, rotation.z),
163+
new FbxVector4 (scale.x, scale.y, scale.z)
164+
);
165+
}
166+
167+
private GameObject CreateHierarchy ()
168+
{
169+
// Create the following hierarchy:
170+
// Root
171+
// -> Parent1
172+
// ----> Child1
173+
// ----> Child2
174+
// -> Parent2
175+
// ----> Child3
176+
177+
var root = CreateGameObject ("Root");
178+
SetTransform (root.transform,
179+
new Vector3 (3, 4, -6),
180+
new Vector3 (45, 10, 34),
181+
new Vector3 (2, 1, 3));
182+
183+
var parent1 = CreateGameObject ("Parent1", root.transform);
184+
SetTransform (parent1.transform,
185+
new Vector3 (53, 0, -1),
186+
new Vector3 (0, 5, 0),
187+
new Vector3 (1, 1, 1));
188+
189+
var parent2 = CreateGameObject ("Parent2", root.transform);
190+
SetTransform (parent2.transform,
191+
new Vector3 (0, 0, 0),
192+
new Vector3 (90, 1, 3),
193+
new Vector3 (1, 0.3f, 0.5f));
194+
195+
parent1.transform.SetAsFirstSibling ();
196+
197+
CreateGameObject ("Child1", parent1.transform);
198+
CreateGameObject ("Child2", parent1.transform);
199+
CreateGameObject ("Child3", parent2.transform);
200+
201+
return root;
202+
}
203+
204+
private void SetTransform(Transform t, Vector3 pos, Vector3 rot, Vector3 scale){
205+
t.localPosition = pos;
206+
t.localEulerAngles = rot;
207+
t.localScale = scale;
208+
}
209+
210+
private GameObject CreateGameObject(string name, Transform parent = null)
211+
{
212+
var go = new GameObject (name);
213+
go.transform.SetParent (parent);
214+
return go;
215+
}
216+
217+
private void CompareHierarchies(
218+
GameObject expectedHierarchy, GameObject actualHierarchy,
219+
bool ignoreName = false, bool compareTransform = true)
220+
{
221+
if (!ignoreName) {
222+
Assert.AreEqual (expectedHierarchy.name, actualHierarchy.name);
223+
}
224+
225+
var expectedTransform = expectedHierarchy.transform;
226+
var actualTransform = actualHierarchy.transform;
227+
228+
if (compareTransform) {
229+
Assert.AreEqual (expectedTransform, actualTransform);
230+
}
231+
232+
Assert.AreEqual (expectedTransform.childCount, actualTransform.childCount);
233+
234+
foreach (Transform expectedChild in expectedTransform) {
235+
var actualChild = actualTransform.Find (expectedChild.name);
236+
Assert.IsNotNull (actualChild);
237+
CompareHierarchies (expectedChild.gameObject, actualChild.gameObject);
238+
}
239+
}
240+
241+
private void CompareHierarchies(GameObject[] expectedHierarchy, GameObject[] actualHierarchy)
242+
{
243+
Assert.AreEqual (expectedHierarchy.Length, actualHierarchy.Length);
244+
245+
System.Array.Sort (expectedHierarchy, delegate (GameObject x, GameObject y) {
246+
return x.name.CompareTo(y.name);
247+
});
248+
System.Array.Sort (actualHierarchy, delegate (GameObject x, GameObject y) {
249+
return x.name.CompareTo(y.name);
250+
});
251+
252+
for (int i = 0; i < expectedHierarchy.Length; i++) {
253+
CompareHierarchies (expectedHierarchy [i], actualHierarchy [i], false, false);
254+
// if we are Comparing lists of hierarchies, that means that the transforms
255+
// should be the global transform of expected, as there is no zeroed out root
256+
CompareGlobalTransform (actualHierarchy [i].transform, expectedHierarchy [i].transform);
257+
}
258+
}
259+
260+
private GameObject ExportSelection(Object[] selected)
261+
{
262+
// export selected to a file, then return the root
263+
var filename = GetRandomFileNamePath();
264+
265+
Debug.unityLogger.logEnabled = false;
266+
var fbxFileName = FbxExporters.Editor.ModelExporter.ExportObjects (filename, selected) as string;
267+
Debug.unityLogger.logEnabled = true;
268+
269+
Assert.IsNotNull (fbxFileName);
270+
271+
// make filepath relative to project folder
272+
if (fbxFileName.StartsWith (Application.dataPath, System.StringComparison.CurrentCulture))
273+
{
274+
fbxFileName = "Assets" + fbxFileName.Substring (Application.dataPath.Length);
275+
}
276+
// refresh the assetdata base so that we can query for the model
277+
AssetDatabase.Refresh ();
278+
279+
Object unityMainAsset = AssetDatabase.LoadMainAssetAtPath (fbxFileName);
280+
var fbxRoot = unityMainAsset as GameObject;
281+
282+
Assert.IsNotNull (fbxRoot);
283+
return fbxRoot;
284+
}
285+
}
286+
}

0 commit comments

Comments
 (0)